diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml new file mode 100644 index 0000000..bab20a3 --- /dev/null +++ b/.github/workflows/single-platform.yml @@ -0,0 +1,101 @@ +name: Build and Run Unit Tests + +on: + push: + branches: [ "main", "dane_dev" ] + pull_request: + branches: [ "main", "dane_dev" ] + +jobs: + testMPI: + runs-on: ubuntu-latest + # Define a build matrix over compilers + strategy: + matrix: + compiler: [GCC, ICC, CLANG] + + steps: + # 1) Check out the repository code + - name: Checkout repository + uses: actions/checkout@v4 + + # 2) Set up Spack for package management + - name: Set-up Spack + uses: spack/setup-spack@v2 + with: + ref: develop # Use the 'develop' branch of the spack/setup-spack action + buildcache: true # Enable Spack binary cache + color: true # Enable colored output + path: spack # Install Spack under ./spack directory + + # 3) Install necessary compiler and MPI packages via Spack + - name: Install Compilers and MPI Wrappers + run: | + # Source Spack environment to get spack commands + . ./spack/share/spack/setup-env.sh + + # Based on matrix.compiler, install the right packages + case "${{ matrix.compiler }}" in + GCC) + # Install OpenMPI for GCC + spack install -j 4 openmpi;; + ICC) + # Install Intel compilers and Intel MPI + spack install -j 4 intel-oneapi-compilers + spack install -j 4 intel-oneapi-mpi;; + CLANG) + # Install LLVM/Clang and OpenMPI + spack install -j 4 llvm + spack install -j 4 openmpi;; + esac + + # 4) Configure, build, and run tests in one step to preserve environment + - name: Configure and Make SparseBench Tests + run: | + # Re-source Spack so we have spack load available + . ./spack/share/spack/setup-env.sh + + # Based on matrix.compiler, load the correct compiler/MPI into this shell + case "${{ matrix.compiler }}" in + GCC) + # Load OpenMPI for GCC + eval "$(spack load --sh openmpi)";; + ICC) + # Load Intel compilers and Intel MPI + eval "$(spack load --sh intel-oneapi-compilers)" + eval "$(spack load --sh intel-oneapi-mpi)";; + CLANG) + # Load LLVM/Clang and OpenMPI + eval "$(spack load --sh llvm)" + eval "$(spack load --sh openmpi)";; + esac + + # Export TOOLCHAIN for the Makefile + export TOOLCHAIN="${{ matrix.compiler }}" + + # Define reusable logic + run_tests() { + local FMT=$1 + echo ">>> Building and testing with $FMT matrix format." + + sed -E -i \ + -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ + -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + -e "s/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\1${FMT}/" \ + -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ + config.mk + + # Build (MPI-only) sparseBench + make + + # Build tests + cd tests && make clean && make + + # Run (single rank) tests + mpirun -n 1 ./runTests + cd .. + } + + # Run tests with both formats + run_tests CRS + run_tests SCS \ No newline at end of file diff --git a/.gitignore b/.gitignore index f907fe6..730666d 100644 --- a/.gitignore +++ b/.gitignore @@ -60,3 +60,4 @@ tests/runTests tests/*.o tests/matrix/*.o tests/data/reported/* +!tests/data/reported/.gitignore diff --git a/mk/include_CLANG.mk b/mk/include_CLANG.mk index e58271e..fc01999 100644 --- a/mk/include_CLANG.mk +++ b/mk/include_CLANG.mk @@ -12,8 +12,11 @@ OPENMP = -fopenmp #OPENMP = -Xpreprocessor -fopenmp #required on Macos with homebrew libomp endif +# Set default +C_VERSION = c17 + VERSION = --version -CFLAGS = -O3 -ffast-math -std=c23 $(OPENMP) +CFLAGS = -O3 -ffast-math -std=$(C_VERSION) $(OPENMP) # CFLAGS = -O0 -g -std=c99 $(OPENMP) LFLAGS = $(OPENMP) DEFINES += -D_GNU_SOURCE diff --git a/mk/include_GCC.mk b/mk/include_GCC.mk index e778723..35ee4df 100644 --- a/mk/include_GCC.mk +++ b/mk/include_GCC.mk @@ -11,8 +11,11 @@ ifeq ($(strip $(ENABLE_OPENMP)),true) OPENMP = -fopenmp endif +# Set default +C_VERSION = c17 + VERSION = --version -CFLAGS = -O3 -ffast-math -std=c23 $(OPENMP) +CFLAGS = -O3 -ffast-math -std=$(C_VERSION) $(OPENMP) # CFLAGS = -O0 -g -std=c99 $(OPENMP) LFLAGS = $(OPENMP) DEFINES += -D_GNU_SOURCE # -DVERBOSE diff --git a/mk/include_ICC.mk b/mk/include_ICC.mk index 5ff76cf..5aeabd3 100644 --- a/mk/include_ICC.mk +++ b/mk/include_ICC.mk @@ -11,8 +11,11 @@ ifeq ($(strip $(ENABLE_OPENMP)),true) OPENMP = -qopenmp endif +# Set default +C_VERSION = c17 + VERSION = --version -CFLAGS = -O3 -ffast-math -xHost -std=c23 $(OPENMP) +CFLAGS = -O3 -ffast-math -xHost -std=$(C_VERSION) $(OPENMP) # CFLAGS = -O0 -g -std=c99 $(OPENMP) LFLAGS = $(OPENMP) DEFINES += -D_GNU_SOURCE # -DVERBOSE diff --git a/src/comm.c b/src/comm.c index 574e595..c74276c 100644 --- a/src/comm.c +++ b/src/comm.c @@ -714,6 +714,7 @@ void commPrintConfig( #endif } +// TODO: Unify matrix dumping void commMatrixDump(Comm* c, Matrix* m) { int rank = c->rank; @@ -726,81 +727,83 @@ void commMatrixDump(Comm* c, Matrix* m) CG_FLOAT* val = m->val; if (commIsMaster(c)) { - printf("Matrix: %d total non zeroes, total number of rows %d\n", + fprintf(c->logFile, + "Matrix: %d total non zeroes, total number of rows %d\n", m->totalNnz, m->totalNr); } for (int i = 0; i < size; i++) { if (i == rank) { - printf("Rank %d: number of rows %d\n", rank, numRows); + fprintf(c->logFile, "Rank %d: number of rows %d\n", rank, numRows); for (int rowID = 0; rowID < numRows; rowID++) { - printf("Row [%d]: ", rowID); + fprintf(c->logFile, "Row [%d]: ", rowID); for (int rowEntry = rowPtr[rowID]; rowEntry < rowPtr[rowID + 1]; rowEntry++) { - printf("[%d]:%.2f ", colInd[rowEntry], val[rowEntry]); + fprintf(c->logFile, "[%d]:%.2f ", colInd[rowEntry], val[rowEntry]); } - printf("\n"); + fprintf(c->logFile, "\n"); } - fflush(stdout); + fflush(c->logFile); } -#ifdef _MPI - MPI_Barrier(MPI_COMM_WORLD); -#endif } #endif /* ifdef CRS */ #ifdef SCS - printf("m->startRow = %d\n", m->startRow); - printf("m->stopRow = %d\n", m->stopRow); - printf("m->totalNr = %d\n", m->totalNr); - printf("m->totalNnz = %d\n", m->totalNnz); - printf("m->nr = %d\n", m->nr); - printf("m->nc = %d\n", m->nc); - printf("m->nnz = %d\n", m->nnz); - printf("m->C = %d\n", m->C); - printf("m->sigma = %d\n", m->sigma); - printf("m->nChunks = %d\n", m->nChunks); - printf("m->nrPadded = %d\n", m->nrPadded); + fprintf(c->logFile, "m->startRow = %d\n", m->startRow); + fprintf(c->logFile, "m->stopRow = %d\n", m->stopRow); + fprintf(c->logFile, "m->totalNr = %d\n", m->totalNr); + fprintf(c->logFile, "m->totalNnz = %d\n", m->totalNnz); + fprintf(c->logFile, "m->nr = %d\n", m->nr); + fprintf(c->logFile, "m->nc = %d\n", m->nc); + fprintf(c->logFile, "m->nnz = %d\n", m->nnz); + fprintf(c->logFile, "m->C = %d\n", m->C); + fprintf(c->logFile, "m->sigma = %d\n", m->sigma); + fprintf(c->logFile, "m->nChunks = %d\n", m->nChunks); + fprintf(c->logFile, "m->nrPadded = %d\n", m->nrPadded); + fprintf(c->logFile, "m->nElems = %d\n", m->nElems); // Dump permutation arrays - printf("oldToNewPerm: "); + fprintf(c->logFile, "oldToNewPerm: "); for (int i = 0; i < m->nr; ++i) { - printf("%d, ", m->oldToNewPerm[i]); + fprintf(c->logFile, "%d, ", m->oldToNewPerm[i]); } - printf("\n"); - printf("newToOldPerm: "); + fprintf(c->logFile, "\n"); + fprintf(c->logFile, "newToOldPerm: "); for (int i = 0; i < m->nr; ++i) { - printf("%d, ", m->newToOldPerm[i]); + fprintf(c->logFile, "%d, ", m->newToOldPerm[i]); } - printf("\n"); + fprintf(c->logFile, "\n"); // Dump chunk data - printf("chunkLens: "); + fprintf(c->logFile, "chunkLens: "); for (int i = 0; i < m->nChunks; ++i) { - printf("%d, ", m->chunkLens[i]); + fprintf(c->logFile, "%d, ", m->chunkLens[i]); } - printf("\n"); - printf("chunkPtr: "); + fprintf(c->logFile, "\n"); + fprintf(c->logFile, "chunkPtr: "); for (int i = 0; i < m->nChunks + 1; ++i) { - printf("%d, ", m->chunkPtr[i]); + fprintf(c->logFile, "%d, ", m->chunkPtr[i]); } - printf("\n"); + fprintf(c->logFile, "\n"); // Dump matrix data - printf("colInd: "); + fprintf(c->logFile, "colInd: "); for (int i = 0; i < m->nElems; ++i) { - printf("%d, ", m->colInd[i]); + fprintf(c->logFile, "%d, ", m->colInd[i]); } - printf("\n"); - printf("val: "); + fprintf(c->logFile, "\n"); + fprintf(c->logFile, "val: "); for (int i = 0; i < m->nElems; ++i) { - printf("%f, ", m->val[i]); + fprintf(c->logFile, "%f, ", m->val[i]); } - printf("\n"); + fprintf(c->logFile, "\n"); #endif /* ifdef SCS */ +#ifdef _MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif } void commVectorDump(Comm* c, CG_FLOAT* v, CG_UINT size, char* name) diff --git a/src/matrix-SCS.c b/src/matrix-SCS.c index 187ee0c..37817e7 100644 --- a/src/matrix-SCS.c +++ b/src/matrix-SCS.c @@ -39,8 +39,8 @@ void convertMatrix(Matrix* m, GMatrix* im) m->nnz = im->nnz; m->nChunks = (m->nr + m->C - 1) / m->C; m->nrPadded = m->nChunks * m->C; - m->C = (CG_UINT)1; - m->sigma = (CG_UINT)1; + // m->C = (CG_UINT)1; + // m->sigma = (CG_UINT)1; // (Temporary array) Assign an index to each row to use for row sorting SellCSigmaPair* elemsPerRow = (SellCSigmaPair*)allocate(ARRAY_ALIGNMENT, diff --git a/tests/Makefile b/tests/Makefile index 8919686..596454f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,11 +1,11 @@ -# # # DL 2025.04.04 +# # # DL 2025.06.25 # # # Collection of unit tests include ../config.mk include ../mk/include_$(TOOLCHAIN).mk BUILD_DIR=../build -TC_DIR=${BUILD_DIR}/$(TOOLCHAIN) +TC_DIR=${BUILD_DIR}/$(MTX_FMT)-$(TOOLCHAIN) # List of test modules MOD1=matrix @@ -18,12 +18,18 @@ LINKS := $(filter-out ${TC_DIR}/main.o, $(wildcard ${TC_DIR}/*.o)) TARGET=runTests # Collect objects from all test modules -MOD1_OBJECTS=${MOD1}/convertSCS.o ${MOD1}/matrixTests.o -MOD2_OBJECTS=${MOD2}/spmvSCS.o ${MOD2}/solverTests.o +MOD1_OBJECTS=${MOD1}/convert$(MTX_FMT).o ${MOD1}/matrixTests.o +MOD2_OBJECTS=${MOD2}/spmv$(MTX_FMT).o ${MOD2}/solverTests.o OBJECTS := $(shell echo $(MOD1_OBJECTS) $(MOD2_OBJECTS) | tr ' ' '\n' | sort -u | tr '\n' ' ') # Always leave debugging flag on -CFLAGS=-g -qopenmp +CFLAGS=-g $(DEFINES) + +# Only enable OpenMP if it defined in the parent config.mk +ifeq ($(strip $(ENABLE_OPENMP)),true) +CFLAGS += $(OPENMP) +endif +# If enabled, MPI wrappers are already in CC .PHONY: all clean @@ -44,12 +50,12 @@ runTests.o: runTests.c $(MOD1)/matrixTests.o: $(MOD1)/matrixTests.c $(CC) $(CFLAGS) -c -o $@ $< -$(MOD1)/convertSCS.o: $(MOD1)/convertSCS.c +$(MOD1)/convert$(MTX_FMT).o: $(MOD1)/convert$(MTX_FMT).c $(CC) $(CFLAGS) -c -o $@ $< # Module 2 tests: solver $(MOD2)/solverTests.o: $(MOD2)/solverTests.c $(CC) $(CFLAGS) -c -o $@ $< -$(MOD2)/spmvSCS.o: $(MOD2)/spmvSCS.c +$(MOD2)/spmv$(MTX_FMT).o: $(MOD2)/spmv$(MTX_FMT).c $(CC) $(CFLAGS) -c -o $@ $< \ No newline at end of file diff --git a/tests/common.h b/tests/common.h index 0d65e27..e96ba6b 100644 --- a/tests/common.h +++ b/tests/common.h @@ -4,68 +4,66 @@ #include #include -#define SET_ARGS(i, C_val, sigma_val) \ -{ \ - args[i]->C = (C_val); \ - args[i]->sigma = (sigma_val); \ -} +#define SET_SCS_ARGS(i, C_val, sigma_val) \ + { \ + args[i].C = (C_val); \ + args[i].sigma = (sigma_val); \ + } typedef int (*TestFunc)(void* config, const char* dataDir); typedef struct { - const char* name; - TestFunc func; + const char* name; + TestFunc func; } Test; typedef struct { -int C; -int sigma; + int C; + int sigma; } Args; #ifndef BUILD_MATRIX_FILE_PATH -#define BUILD_MATRIX_FILE_PATH(entry, dir, expect, C_str, sigma_str, path) \ -{ \ - strcpy((path), "data/"); \ - strcat((path), (dir)); \ - strcat((path), (entry)->d_name); \ - strcat((path), "_C_"); \ - strcat((path), (C_str)); \ - strcat((path), "_sigma_"); \ - strcat((path), (sigma_str)); \ - strcat((path), (expect)); \ -} +#define BUILD_MATRIX_FILE_PATH(entry, dir, expect, C_str, sigma_str, path) \ + { \ + strcpy((path), "data/"); \ + strcat((path), (dir)); \ + strcat((path), (entry)->d_name); \ + strcat((path), "_C_"); \ + strcat((path), (C_str)); \ + strcat((path), "_sigma_"); \ + strcat((path), (sigma_str)); \ + strcat((path), (expect)); \ + } #endif -#ifndef FORMAT_AND_STRIP_MATRIX_FILE -#define FORMAT_AND_STRIP_MATRIX_FILE(A, entry, C_str, sigma_str) \ -{ \ - sprintf((C_str), "%d", (A).C); \ - sprintf((sigma_str), "%d", (A).sigma); \ - char *dot = strrchr((entry)->d_name, '.'); \ - if (dot != NULL) { \ - *dot = '\0'; \ - } \ -} +#ifndef STRIP_MATRIX_FILE +#define STRIP_MATRIX_FILE(entry) \ + { \ + char* dot = strrchr((entry)->d_name, '.'); \ + if (dot != NULL) { \ + *dot = '\0'; \ + } \ + } #endif #ifndef BUILD_VECTOR_FILE_PATH -#define BUILD_VECTOR_FILE_PATH(entry, dir, expect, path) \ -{ \ - strcpy((path), "data/"); \ - strcat((path), (dir)); \ - strcat((path), (entry)->d_name); \ - strcat((path), (expect)); \ -} +#define BUILD_VECTOR_FILE_PATH(entry, dir, expect, path) \ + { \ + strcpy((path), "data/"); \ + strcat((path), (dir)); \ + strcat((path), (entry)->d_name); \ + strcat((path), (expect)); \ + } #endif #ifndef FORMAT_AND_STRIP_VECTOR_FILE -#define FORMAT_AND_STRIP_VECTOR_FILE(entry) \ -{ \ - char *dot = strrchr((entry)->d_name, '.'); \ - if (dot != NULL) { \ - *dot = '\0'; \ - } \ -} +#define FORMAT_AND_STRIP_VECTOR_FILE(entry) \ + { \ + char* dot = strrchr((entry)->d_name, '.'); \ + if (dot != NULL) { \ + *dot = '\0'; \ + } \ + } #endif #ifndef STR_LEN @@ -77,54 +75,71 @@ int sigma; #define ARRAY_ALIGNMENT 64 #endif -static int diff_files(const char *expectedData, const char *reportedData) { - FILE *f1 = fopen(expectedData, "r"); - FILE *f2 = fopen(reportedData, "r"); - - if (f1 == NULL || f2 == NULL) { - perror("Error opening file"); - return 1; - } - - char line1[2048], line2[2048]; - int line_number = 1; // Line number counter - - // Compare lines until one of the files ends - while (fgets(line1, sizeof(line1), f1) != NULL && fgets(line2, sizeof(line2), f2) != NULL) { - if (strcmp(line1, line2) != 0) { // If the lines are different - printf("Files differ at line %d:\n", line_number); - printf("File 1 (%s): %s\n", expectedData, line1); - printf("File 2 (%s): %s\n", reportedData, line2); - fclose(f1); - fclose(f2); - return 1; // Return 1 as soon as a difference is found - } - line_number++; - } - - // Handle case where one file has more lines - if (fgets(line1, sizeof(line1), f1) != NULL || fgets(line2, sizeof(line2), f2) != NULL) { - printf("Files differ at line %d:\n", line_number); - if (fgets(line1, sizeof(line1), f1) != NULL) { - printf("File 1 (%s): %s\n", expectedData, line1); - } else { - printf("File 1 (%s): (no more lines)\n", expectedData); - } - - if (fgets(line2, sizeof(line2), f2) != NULL) { - printf("File 2 (%s): %s\n", reportedData, line2); - } else { - printf("File 2 (%s): (no more lines)\n", reportedData); - } - - fclose(f1); - fclose(f2); - return 1; // Files are different if one ends before the other - } - - fclose(f1); - fclose(f2); - return 0; // Files are identical +static int diff_files( + const char* expectedData, const char* reportedData, int offset) +{ + FILE* f1 = fopen(expectedData, "r"); + FILE* f2 = fopen(reportedData, "r"); + + if (f1 == NULL || f2 == NULL) { + perror("Error opening file"); + return 1; + } + + char line1[2048], line2[2048]; + int line_number = 1; // Line number counter + + // Skip offset lines in reported file (f2) + for (int i = 0; i < offset; i++) { + if (fgets(line1, sizeof(line1), f2) == NULL) { + printf("Reported file %s has fewer than %d lines to skip.\n", + reportedData, + offset); + fclose(f1); + fclose(f2); + return 1; + } + line_number++; + } + + // Compare lines until one of the files ends + while (fgets(line1, sizeof(line1), f1) != NULL && + fgets(line2, sizeof(line2), f2) != NULL) { + if (strcmp(line1, line2) != 0) { // If the lines are different + printf("Files differ at line %d:\n", line_number); + printf("File 1 (%s): %s\n", expectedData, line1); + printf("File 2 (%s): %s\n", reportedData, line2); + fclose(f1); + fclose(f2); + return 1; // Return 1 as soon as a difference is found + } + line_number++; + } + + // Handle case where one file has more lines + if (fgets(line1, sizeof(line1), f1) != NULL || + fgets(line2, sizeof(line2), f2) != NULL) { + printf("Files differ at line %d:\n", line_number); + if (fgets(line1, sizeof(line1), f1) != NULL) { + printf("File 1 (%s): %s\n", expectedData, line1); + } else { + printf("File 1 (%s): (no more lines)\n", expectedData); + } + + if (fgets(line2, sizeof(line2), f2) != NULL) { + printf("File 2 (%s): %s\n", reportedData, line2); + } else { + printf("File 2 (%s): (no more lines)\n", reportedData); + } + + fclose(f1); + fclose(f2); + return 1; // Files are different if one ends before the other + } + + fclose(f1); + fclose(f2); + return 0; // Files are identical } #endif //__COMMON_H_ diff --git a/tests/data/expected/test0_CRS.in b/tests/data/expected/test0_CRS.in new file mode 100644 index 0000000..3a02ae6 --- /dev/null +++ b/tests/data/expected/test0_CRS.in @@ -0,0 +1,12 @@ +Matrix: 18 total non zeroes, total number of rows 10 +Rank 0: number of rows 10 +Row [0]: [0]:11.00 [3]:14.00 [4]:15.00 +Row [1]: [1]:22.00 +Row [2]: [0]:31.00 [1]:32.00 [2]:33.00 +Row [3]: [3]:44.00 +Row [4]: [4]:55.00 +Row [5]: [5]:66.00 [8]:69.00 [9]:610.00 +Row [6]: [6]:77.00 +Row [7]: [5]:86.00 [6]:87.00 [7]:88.00 +Row [8]: [8]:99.00 +Row [9]: [9]:1010.00 diff --git a/tests/data/expected/test0_spmv_x_1.in b/tests/data/expected/test0_spmv_x_1.in index caaf204..a489381 100644 --- a/tests/data/expected/test0_spmv_x_1.in +++ b/tests/data/expected/test0_spmv_x_1.in @@ -1 +1,10 @@ -vec = 40.000000, 22.000000, 96.000000, 44.000000, 55.000000, 745.000000, 77.000000, 261.000000, 99.000000, 1010.000000, \ No newline at end of file + element[0] 40.000000 + element[1] 22.000000 + element[2] 96.000000 + element[3] 44.000000 + element[4] 55.000000 + element[5] 745.000000 + element[6] 77.000000 + element[7] 261.000000 + element[8] 99.000000 + element[9] 1010.000000 diff --git a/tests/data/expected/test8_CRS.in b/tests/data/expected/test8_CRS.in new file mode 100644 index 0000000..acfcfb4 --- /dev/null +++ b/tests/data/expected/test8_CRS.in @@ -0,0 +1,12 @@ +Matrix: 27 total non zeroes, total number of rows 10 +Rank 0: number of rows 10 +Row [0]: [0]:11.00 [3]:14.00 [4]:15.00 [9]:110.00 +Row [1]: [0]:21.00 [1]:22.00 +Row [2]: [0]:31.00 [1]:32.00 [2]:33.00 +Row [3]: [0]:41.00 [3]:44.00 +Row [4]: [0]:51.00 [4]:55.00 +Row [5]: [0]:61.00 [5]:66.00 [8]:69.00 [9]:610.00 +Row [6]: [0]:71.00 [6]:77.00 +Row [7]: [0]:81.00 [5]:86.00 [6]:87.00 [7]:88.00 +Row [8]: [0]:91.00 [8]:99.00 +Row [9]: [0]:101.00 [9]:1010.00 diff --git a/tests/data/expected/test8_spmv_x_1.in b/tests/data/expected/test8_spmv_x_1.in new file mode 100644 index 0000000..050d93e --- /dev/null +++ b/tests/data/expected/test8_spmv_x_1.in @@ -0,0 +1,10 @@ + element[0] 150.000000 + element[1] 43.000000 + element[2] 96.000000 + element[3] 85.000000 + element[4] 106.000000 + element[5] 806.000000 + element[6] 148.000000 + element[7] 342.000000 + element[8] 190.000000 + element[9] 1111.000000 diff --git a/tests/data/reported/.gitignore b/tests/data/reported/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/tests/matrix/convertCRS.c b/tests/matrix/convertCRS.c new file mode 100644 index 0000000..54eafec --- /dev/null +++ b/tests/matrix/convertCRS.c @@ -0,0 +1,114 @@ +// DL 2025.06.25 +// Unit test: Converts a Matrix Market (MM) matrix to internal CRS format +// and compares against expected output from disk. +// Assumes a single MPI rank (no parallel communication). + +#include "../../src/comm.h" +#include "../../src/matrix.h" +#include "../common.h" +#include +#include +#include +#include + +/** + * Runs conversion tests for all `.mtx` files in `dataDir/testMatrices/`. + * + * For each matrix: + * 1. Builds full path to the file + * 2. Loads MM matrix and converts it to GMatrix, and then CRS + * 3. Dumps the converted result to a file + * 4. Compares the dump against expected result (if present) + * + * Returns 0 on success, 1 if any test fails. + */ +int test_convertCRS(void* args, const char* dataDir) +{ + // Compose path to directory containing test matrices + char matricesPath[STR_LEN]; + snprintf(matricesPath, STR_LEN, "%s%s", dataDir, "testMatrices/"); + + // Open the directory and check for errors + DIR* dir = opendir(matricesPath); + if (dir == NULL) { + perror("Error opening directory"); + return 1; + } + + // Read the directory entries + struct dirent* entry; + + // Iterate through files in the directory + while ((entry = readdir(dir)) != NULL) { + // Only process files with ".mtx" extension + if (strstr(entry->d_name, ".mtx") != NULL) { + + // Build full path to matrix file + char matrixPath[STR_LEN]; + snprintf(matrixPath, STR_LEN, "%s%s", matricesPath, entry->d_name); + + MMMatrix M; + Args* arguments = (Args*)args; + + STRIP_MATRIX_FILE(entry) // Removes .mtx from file name + + // Path to expected output (".in" file) for this matrix + char pathToExpectedData[STR_LEN]; + snprintf(pathToExpectedData, + STR_LEN, + "data/expected/%s_CRS.in", + entry->d_name); + + // Only test if expected file exists + if (fopen(pathToExpectedData, "r")) { + + // Path to write reported output (".out" file) + char pathToReportedData[STR_LEN]; + snprintf(pathToReportedData, + STR_LEN, + "data/reported/%s_CRS.out", + entry->d_name); + + // Open output file for writing results + FILE* reportedData = fopen(pathToReportedData, "w"); + if (reportedData == NULL) { + perror("fopen failed for reportedData"); + exit(EXIT_FAILURE); // Crash fast on I/O error + } + + // Load matrix from Matrix Market file + MMMatrixRead(&M, matrixPath); + + // Declare graph and algebraic matrices + GMatrix m; + Matrix A; + Comm c; + + // Set single-rank defaults (no parallel distribution) + M.startRow = 0; + M.stopRow = M.nr; + M.totalNr = M.nr; + M.totalNnz = M.nnz; + c.rank = 0; + c.size = 1; + c.logFile = reportedData; + + // Convert to internal formats + matrixConvertfromMM(&M, &m); // MM → GMatrix + convertMatrix(&A, &m); // GMatrix → Matrix + commMatrixDump(&c, &A); // Output formatted result + fclose(reportedData); + + // Diff against expected file — if mismatch, fail the test + if (diff_files(pathToExpectedData, pathToReportedData, 0)) { + closedir(dir); + return 1; + } + } + } + } + + // Clean up and return success + closedir(dir); + return 0; +} diff --git a/tests/matrix/convertCRS.h b/tests/matrix/convertCRS.h new file mode 100644 index 0000000..596fdff --- /dev/null +++ b/tests/matrix/convertCRS.h @@ -0,0 +1,6 @@ +#ifndef __convertCRS_H_ +#define __convertCRS_H_ + +int test_convertCRS(void* args, const char* dataDir); + +#endif // __convertCRS_H_ diff --git a/tests/matrix/convertSCS.c b/tests/matrix/convertSCS.c index ce391c0..b6fe21f 100644 --- a/tests/matrix/convertSCS.c +++ b/tests/matrix/convertSCS.c @@ -1,91 +1,127 @@ -// DL 2025.04.04 -// Single rank test to convert MM to SCS format +// DL 2025.06.25 +// Unit test: Converts a Matrix Market (MM) matrix to internal SCS format +// and compares against expected output from disk. +// Assumes a single MPI rank (no parallel communication). +#include "../../src/comm.h" +#include "../../src/matrix.h" +#include "../common.h" +#include #include #include -#include #include -#include "../../src/matrix.h" -#include "../common.h" -int test_convertSCS(void* args, const char* dataDir){ - - int rank = 0; - int size = 1; - - // Open the directory - char *pathToMatrices = malloc(strlen(dataDir) + strlen("testMatrices/") + 1); - strcpy(pathToMatrices, dataDir); - strcat(pathToMatrices, "testMatrices/"); - - DIR *dir = opendir( pathToMatrices ); - if (dir == NULL) { - perror("Error opening directory"); - return 1; - } - - // Read the directory entries - struct dirent *entry; - while ((entry = readdir(dir)) != NULL) { - if (strstr(entry->d_name, ".mtx") != NULL){ - char *pathToMatrix = malloc(strlen(pathToMatrices) + strlen(entry->d_name) + 1); - strcpy(pathToMatrix, pathToMatrices); - strcat(pathToMatrix, entry->d_name); - - Matrix A; - Args* arguments = (Args*)args; - A.C = arguments->C; - A.sigma = arguments->sigma; - - // String preprocessing - char C_str[STR_LEN]; - char sigma_str[STR_LEN]; - FORMAT_AND_STRIP_MATRIX_FILE(A, entry, C_str, sigma_str) - - // This is the external file to check against - char *pathToExpectedData = malloc(STR_LEN); - BUILD_MATRIX_FILE_PATH(entry, "expected/", ".in", C_str, sigma_str, pathToExpectedData); - - // Validate against expected data, if it exists - if(fopen(pathToExpectedData, "r")){ - - MmMatrix m; - matrixRead( &m, pathToMatrix ); - - // Set single rank defaults for MmMatrix - m.startRow = 0; - m.stopRow = m.nr; - m.totalNr = m.nr; - m.totalNnz = m.nnz; - - matrixConvertMMtoSCS(&m, &A, rank, size); - - // Dump to this external file - char *pathToReportedData = malloc(STR_LEN); - BUILD_MATRIX_FILE_PATH(entry, "reported/", ".out", C_str, sigma_str, pathToReportedData); - FILE *reportedData = fopen(pathToReportedData, "w"); - - dumpSCSMatrixToFile(&A, reportedData); - fclose(reportedData); - - // If the expect and reported data differ in some way - if(diff_files(pathToExpectedData, pathToReportedData)){ - free(pathToReportedData); - free(pathToExpectedData); - free(pathToMatrix); - - closedir(dir); - return 1; - } - } - - free(pathToExpectedData); - free(pathToMatrix); - } - } - - free(pathToMatrices); - closedir(dir); - - return 0; -} \ No newline at end of file +/** + * Runs conversion tests for all `.mtx` files in `dataDir/testMatrices/`. + * + * For each matrix: + * 1. Builds full path to the file + * 2. Parses metadata for C and sigma + * 3. Loads MM matrix and converts it to GMatrix, and then SCS + * 4. Dumps the converted result to a file + * 5. Compares the dump against expected result (if present) + * + * Returns 0 on success, 1 if any test fails. + */ +int test_convertSCS(void* args, const char* dataDir) +{ + // Compose path to directory containing test matrices + char matricesPath[STR_LEN]; + snprintf(matricesPath, STR_LEN, "%s%s", dataDir, "testMatrices/"); + + // Open the directory and check for errors + DIR* dir = opendir(matricesPath); + if (dir == NULL) { + perror("Error opening directory"); + return 1; + } + + // Read the directory entries + struct dirent* entry; + + // Iterate through files in the directory + while ((entry = readdir(dir)) != NULL) { + // Only process files with ".mtx" extension + if (strstr(entry->d_name, ".mtx") != NULL) { + + // Build full path to matrix file + char matrixPath[STR_LEN]; + snprintf(matrixPath, STR_LEN, "%s%s", matricesPath, entry->d_name); + + MMMatrix M; + Args* arguments = (Args*)args; + + // Extract C and sigma values from filename, using helper macro + char C_str[STR_LEN]; + char sigma_str[STR_LEN]; + sprintf(C_str, "%d", arguments->C); + sprintf(sigma_str, "%d", arguments->sigma); + + STRIP_MATRIX_FILE(entry) // Removes .mtx from file name + + // Path to expected output (".in" file) for this matrix + char pathToExpectedData[STR_LEN]; + snprintf(pathToExpectedData, + STR_LEN, + "data/expected/%s_C_%s_sigma_%s.in", + entry->d_name, + C_str, + sigma_str); + + // Only test if expected file exists + if (fopen(pathToExpectedData, "r")) { + + // Path to write reported output (".out" file) + char pathToReportedData[STR_LEN]; + snprintf(pathToReportedData, + STR_LEN, + "data/reported/%s_C_%s_sigma_%s.out", + entry->d_name, + C_str, + sigma_str); + + // Open output file for writing results + FILE* reportedData = fopen(pathToReportedData, "w"); + if (reportedData == NULL) { + perror("fopen failed for reportedData"); + exit(EXIT_FAILURE); // Crash fast on I/O error + } + + // Read matrix from Matrix Market file + MMMatrixRead(&M, matrixPath); + + // Declare matrix and communication structs + GMatrix m; + Matrix A; + Comm c; + + // Set single-rank defaults + M.startRow = 0; + M.stopRow = M.nr; + M.totalNr = M.nr; + M.totalNnz = M.nnz; + c.rank = 0; + c.size = 1; + c.logFile = reportedData; + + // Convert to internal formats + matrixConvertfromMM(&M, &m); // MM → GMatrix + A.C = arguments->C; + A.sigma = arguments->sigma; + convertMatrix(&A, &m); // GMatrix → Matrix + commMatrixDump(&c, &A); // Output formatted result + fclose(reportedData); + + // Diff against expected file — if mismatch, fail the test + if (diff_files(pathToExpectedData, pathToReportedData, 0)) { + closedir(dir); + return 1; + } + } + } + } + + // Clean up and return success + closedir(dir); + return 0; +} diff --git a/tests/matrix/matrixTests.c b/tests/matrix/matrixTests.c index 7bac81d..a8f85c4 100644 --- a/tests/matrix/matrixTests.c +++ b/tests/matrix/matrixTests.c @@ -1,68 +1,59 @@ -#include "convertSCS.h" #include "../common.h" +#ifdef CRS +#include "convertCRS.h" +#endif +#ifdef SCS +#include "convertSCS.h" +#endif +#include #include #include -#include #include -int matrixTests(int argc, char** argv){ - // Hard-code data directory - char* dataDir = malloc(6); - if (dataDir) strcpy(dataDir, "data/"); - - // Alternatively, if you want to get the data dir from the command line - // Check if the user has provided the directory path - // if (argc != 2) { - // fprintf(stderr, "Usage: %s \n", argv[0]); - // return 1; // Exit with error code if not provided - // } - - // Get the directory path from the command line argument - // const char *dataDir = argv[1]; - - Test tests[] = { - { "convertSell-1-1", test_convertSCS }, // Test 1 - { "convertSell-2-1", test_convertSCS }, // Test 2 - { "convertSell-4-1", test_convertSCS } // Test 3 - // Add more here... - }; - - int num_tests = sizeof(tests) / sizeof(tests[0]); - int passed = 0; - - Args** args = (Args **)malloc(num_tests * sizeof(Args*)); - for (int i = 0; i < num_tests; ++i) { - args[i] = (Args*)malloc(sizeof(Args)); - if (!args[i]) { - printf("Memory allocation failed for test %d!\n", i); - return 1; - } - } - - // Manually assign one configuration per test - SET_ARGS(0, 1, 1); // Test 1 - SET_ARGS(1, 2, 1); // Test 2 - SET_ARGS(2, 4, 1); // Test 3 - - printf("Running %d Matrix tests:\n", num_tests); - for (int i = 0; i < num_tests; ++i) { - printf("[%-2d/%-2d] %-20s ... \n", i+1, num_tests, tests[i].name); - fflush(stdout); - - if (!(tests[i].func((void*)args[i], dataDir))) { - printf("✅ PASS\n"); - passed++; - } else { - printf("❌ FAIL\n"); - } - } - - printf("\nSummary: %d/%d Matrix tests passed.\n", passed, num_tests); - - free(dataDir); - free(args); - - return (passed == num_tests) ? 0 : 1; +int matrixTests(int argc, char** argv) +{ + const char* dataDir = "data/"; + + Test tests[] = { +#ifdef CRS + { "convertCRS", test_convertCRS }, +#endif +#ifdef SCS + { "convertSell-1-1", test_convertSCS }, // Test 1 + { "convertSell-2-1", test_convertSCS }, // Test 2 + { "convertSell-4-1", test_convertSCS }, // Test 3 +#endif + // Add more here... + }; + + int num_tests = sizeof(tests) / sizeof(tests[0]); + int passed = 0; + + Args args[num_tests]; + +#ifdef SCS + // Manually assign one (C, sigma) configuration per test + SET_SCS_ARGS(0, 1, 1); // Test 1 + SET_SCS_ARGS(1, 2, 1); // Test 2 + SET_SCS_ARGS(2, 4, 1); // Test 3 +#endif + + printf("Running %d Matrix tests:\n", num_tests); + for (int i = 0; i < num_tests; ++i) { + printf("[%-2d/%-2d] %-20s ... \n", i + 1, num_tests, tests[i].name); + fflush(stdout); + + if (!(tests[i].func(&args[i], dataDir))) { + printf("✅ PASS\n\n"); + passed++; + } else { + printf("❌ FAIL\n\n"); + } + } + + printf("Summary: %d/%d Matrix tests passed.\n", passed, num_tests); + + return (passed == num_tests) ? 0 : 1; } \ No newline at end of file diff --git a/tests/runTests.c b/tests/runTests.c index ec7c6d5..643c68c 100644 --- a/tests/runTests.c +++ b/tests/runTests.c @@ -1,4 +1,3 @@ - #include #include #include @@ -6,9 +5,22 @@ #include "matrix/matrixTests.h" #include "solver/solverTests.h" -int main(int argc, char** argv){ - matrixTests(argc, argv); - solverTests(argc, argv); - - return 0; +#ifdef _MPI +#include +#endif + +int main(int argc, char** argv) +{ +#ifdef _MPI + MPI_Init(&argc, &argv); +#endif + + matrixTests(argc, argv); + solverTests(argc, argv); + +#ifdef _MPI + MPI_Finalize(); +#endif + + return 0; } \ No newline at end of file diff --git a/tests/solver/solverTests.c b/tests/solver/solverTests.c index 0ae44ac..0cfd21b 100644 --- a/tests/solver/solverTests.c +++ b/tests/solver/solverTests.c @@ -1,69 +1,58 @@ -#include "spmvSCS.h" #include "../common.h" +#ifdef CRS +#include "spmvCRS.h" +#endif +#ifdef SCS +#include "spmvSCS.h" +#endif +#include #include #include -#include #include -int solverTests(int argc, char** argv){ - // Hard-code data directory - char* dataDir = malloc(6); - if (dataDir) strcpy(dataDir, "data/"); - - // Alternatively, if you want to get the data dir from the command line - // Check if the user has provided the directory path - // if (argc != 2) { - // fprintf(stderr, "Usage: %s \n", argv[0]); - // return 1; // Exit with error code if not provided - // } - - // Get the directory path from the command line argument - // const char *dataDir = argv[1]; - - Test tests[] = { - { "SpMV CRS", test_spmvSCS }, // Test 1 - { "SpMV Sell-1-1", test_spmvSCS }, // Test 2 - { "SpMV Sell-2-1", test_spmvSCS }, // Test 3 - { "SpMV Sell-4-1", test_spmvSCS } // Test 4 - // Add more here... - }; - - int num_tests = sizeof(tests) / sizeof(tests[0]); - int passed = 0; - - Args** args = (Args **)malloc(num_tests * sizeof(Args*)); - for (int i = 0; i < num_tests; ++i) { - args[i] = (Args*)malloc(sizeof(Args)); - if (!args[i]) { - printf("Memory allocation failed for test %d!\n", i); - return 1; - } - } - - // Manually assign one configuration per test - SET_ARGS(0, 0, 0); // Test 1 - SET_ARGS(1, 1, 1); // Test 2 - SET_ARGS(2, 2, 1); // Test 3 - SET_ARGS(3, 4, 1); // Test 4 - - printf("Running %d Solver tests:\n", num_tests); - for (int i = 0; i < num_tests; ++i) { - printf("[%-2d/%-2d] %-20s ... \n", i+1, num_tests, tests[i].name); - fflush(stdout); - - if (!(tests[i].func((void*)args[i], dataDir))) { - printf("✅ PASS\n"); - passed++; - } else { - printf("❌ FAIL\n"); - } - } - - printf("\nSummary: %d/%d Solver tests passed.\n", passed, num_tests); - - free(dataDir); - free(args); - - return (passed == num_tests) ? 0 : 1; +int solverTests(int argc, char** argv) +{ + const char* dataDir = "data/"; + + Test tests[] = { +#ifdef CRS + { "SpMV CRS", test_spmvCRS }, +#endif +#ifdef SCS + { "SpMV Sell-1-1", test_spmvSCS }, // Test 1 + { "SpMV Sell-2-1", test_spmvSCS }, // Test 2 + { "SpMV Sell-4-1", test_spmvSCS }, // Test 3 +#endif + // Add more here... + }; + + int num_tests = sizeof(tests) / sizeof(tests[0]); + int passed = 0; + + Args args[num_tests]; + +#ifdef SCS + // Manually assign one configuration per test + SET_SCS_ARGS(0, 1, 1); // Test 1 + SET_SCS_ARGS(1, 2, 1); // Test 2 + SET_SCS_ARGS(2, 4, 1); // Test 3 +#endif + + printf("Running %d Solver tests:\n", num_tests); + for (int i = 0; i < num_tests; ++i) { + printf("[%-2d/%-2d] %-20s ... \n", i + 1, num_tests, tests[i].name); + fflush(stdout); + + if (!(tests[i].func(&args[i], dataDir))) { + printf("✅ PASS\n"); + passed++; + } else { + printf("❌ FAIL\n"); + } + } + + printf("\nSummary: %d/%d Solver tests passed.\n", passed, num_tests); + + return (passed == num_tests) ? 0 : 1; } \ No newline at end of file diff --git a/tests/solver/spmvCRS.c b/tests/solver/spmvCRS.c new file mode 100644 index 0000000..df4f4f8 --- /dev/null +++ b/tests/solver/spmvCRS.c @@ -0,0 +1,136 @@ +// DL 2025.06.25 +// Unit test: Performs SpMV (sparse matrix-vector multiplication) in CRS format +// and compares against expected output from disk. +// Assumes a single MPI rank (no parallel communication). + +#include "../../src/allocate.h" +#include "../../src/comm.h" +#include "../../src/debugger.h" +#include "../../src/matrix.h" +#include "../../src/solver.h" +#include "../common.h" +#include +#include +#include +#include + +#ifdef _OPENMP +#include "../../src/affinity.h" +#include +#endif + +/** + * Runs SpMV tests for all `.mtx` files in `dataDir/testMatrices/`. + * + * For each matrix: + * 1. Loads the file as a Matrix Market input + * 2. Computes y = A * x with x = 1 + * 3. Dumps the result to a file + * 4. Compares against expected result (if present) + * + * Returns 0 on success, 1 if any test fails. + */ + +int test_spmvCRS(void* args, const char* dataDir) +{ + // Compose path to directory containing test matrices + char matricesPath[STR_LEN]; + snprintf(matricesPath, STR_LEN, "%s%s", dataDir, "testMatrices/"); + + // Open the matrix directory for reading + DIR* dir = opendir(matricesPath); + if (dir == NULL) { + perror("Error opening directory"); + return 1; + } + + struct dirent* entry; + + // Iterate through files in the directory + while ((entry = readdir(dir)) != NULL) { + // Only process files with ".mtx" extension + if (strstr(entry->d_name, ".mtx") != NULL) { + + // Build full path to matrix file + char matrixPath[STR_LEN]; + snprintf(matrixPath, STR_LEN, "%s%s", matricesPath, entry->d_name); + + MMMatrix M; + Args* arguments = (Args*)args; + + FORMAT_AND_STRIP_VECTOR_FILE(entry); + + // Path to expected output (".in" file) for this matrix + char pathToExpectedData[STR_LEN]; + snprintf(pathToExpectedData, + STR_LEN, + "data/expected/%s_spmv_x_1.in", + entry->d_name); + + // Only test if expected file exists + if (fopen(pathToExpectedData, "r")) { + + // Path to write reported output (".out" file) + char pathToReportedData[STR_LEN]; + snprintf(pathToReportedData, + STR_LEN, + "data/reported/%s_CRS_spmv_x_1.out", + entry->d_name); + + // Open output file for writing results + FILE* reportedData = fopen(pathToReportedData, "w"); + if (reportedData == NULL) { + perror("fopen failed for reportedData"); + exit(EXIT_FAILURE); + } + + // Read matrix from Matrix Market file + MMMatrixRead(&M, matrixPath); + + // Declare matrix and communication structs + GMatrix m; + Matrix A; + Comm c; + + // Set single-rank defaults + M.startRow = 0; + M.stopRow = M.nr; + M.totalNr = M.nr; + M.totalNnz = M.nnz; + c.rank = 0; + c.size = 1; + c.logFile = reportedData; + + // Convert to internal formats + matrixConvertfromMM(&M, &m); // MM → GMatrix + convertMatrix(&A, &m); // GMatrix → Matrix + + CG_FLOAT* x = (CG_FLOAT*)allocate(ARRAY_ALIGNMENT, + A.nr * sizeof(CG_FLOAT)); + CG_FLOAT* y = (CG_FLOAT*)allocate(ARRAY_ALIGNMENT, + A.nr * sizeof(CG_FLOAT)); + + for (int i = 0; i < A.nr; ++i) { + x[i] = (CG_FLOAT)1.0; + y[i] = (CG_FLOAT)0.0; + } + + spMVM(&A, x, y); + // Output formatted result + commVectorDump(&c, y, A.nr, pathToReportedData); + fclose(reportedData); + + // Diff against expected file — if mismatch, fail the test + // Need to offset the reported data by 1 line + if (diff_files(pathToExpectedData, pathToReportedData, 1)) { + closedir(dir); + return 1; + } + } + } + } + + // Cleanup and exit + closedir(dir); + return 0; +} diff --git a/tests/solver/spmvCRS.h b/tests/solver/spmvCRS.h new file mode 100644 index 0000000..00d647b --- /dev/null +++ b/tests/solver/spmvCRS.h @@ -0,0 +1,6 @@ +#ifndef __spmvCRS_H_ +#define __spmvCRS_H_ + +int test_spmvCRS(void* args, const char* dataDir); + +#endif // __spmvCRS_H_ diff --git a/tests/solver/spmvSCS.c b/tests/solver/spmvSCS.c index 48dfd73..227e638 100644 --- a/tests/solver/spmvSCS.c +++ b/tests/solver/spmvSCS.c @@ -1,140 +1,146 @@ -// DL 2025.04.07 -// Single rank SpMV test +// DL 2025.06.25 +// Unit test: Performs SpMV (sparse matrix-vector multiplication) in SCS format +// and compares against expected output from disk. +// Assumes a single MPI rank (no parallel communication). -#include -#include -#include -#include +#include "../../src/allocate.h" +#include "../../src/comm.h" +#include "../../src/debugger.h" #include "../../src/matrix.h" #include "../../src/solver.h" -#include "../../src/debugger.h" -#include "../../src/allocate.h" #include "../common.h" +#include +#include +#include +#include #ifdef _OPENMP #include "../../src/affinity.h" #include #endif -int test_spmvSCS(void* args, const char* dataDir){ - - int rank = 0; - int size = 1; - int validFileCount = 0; - - // Open the directory - char *pathToMatrices = malloc(strlen(dataDir) + strlen("testMatrices/") + 1); - strcpy(pathToMatrices, dataDir); - strcat(pathToMatrices, "testMatrices/"); - DIR *dir = opendir( pathToMatrices ); - if (dir == NULL) { - perror("Error opening directory"); - return 1; - } - - // Read the directory entries - struct dirent *entry; - while ((entry = readdir(dir)) != NULL) { - if (strstr(entry->d_name, ".mtx") != NULL){ - char *pathToMatrix = malloc(strlen(pathToMatrices) + strlen(entry->d_name) + 1); - strcpy(pathToMatrix, pathToMatrices); - strcat(pathToMatrix, entry->d_name); - - printf("pathToMatrix = %s\n", pathToMatrix); - - Matrix A; - Args* arguments = (Args*)args; - A.C = arguments->C; - A.sigma = arguments->sigma; - char C_str[STR_LEN]; - char sigma_str[STR_LEN]; - sprintf(C_str, "%d", A.C); - sprintf(sigma_str, "%d", A.sigma); - - // String preprocessing - FORMAT_AND_STRIP_VECTOR_FILE(entry) - - // This is the external file to check against - char *pathToExpectedData = malloc(STR_LEN); - BUILD_VECTOR_FILE_PATH(entry, "expected/", "_spmv_x_1.in", pathToExpectedData); - - printf("pathToExpectedData = %s\n", pathToExpectedData); - - // Validate against expected data, if it exists - - if(fopen(pathToExpectedData, "r")){ - ++validFileCount; - - MmMatrix m; - matrixRead( &m, pathToMatrix ); - - // Set single rank defaults for MmMatrix - m.startRow = 0; - m.stopRow = m.nr; - m.totalNr = m.nr; - m.totalNnz = m.nnz; - - int vectorSize; - char* matrixFormat = (char*)malloc(4*sizeof(char)); - - if(A.C == 0 || A.sigma == 0){ - matrixConvertMMtoCRS(&m, &A, rank, size); - vectorSize = A.nr; - strcpy(matrixFormat, "CRS"); - } - else{ - matrixConvertMMtoSCS(&m, &A, rank, size); - vectorSize = A.nrPadded; - strcpy(matrixFormat, "SCS"); - } - VALIDATE_MATRIX_FORMAT(matrixFormat); - A.matrixFormat = matrixFormat; - - CG_FLOAT* x = (CG_FLOAT*)allocate(ARRAY_ALIGNMENT, vectorSize * sizeof(CG_FLOAT)); - CG_FLOAT* y = (CG_FLOAT*)allocate(ARRAY_ALIGNMENT, vectorSize * sizeof(CG_FLOAT)); - - // Fix x = 1 for now - for(int i = 0; i < vectorSize; ++i){ - x[i] = (CG_FLOAT)1.0; - y[i] = (CG_FLOAT)0.0; - } - - spMVM(&A, x, y); - - // Dump to this external file - char *pathToReportedData = malloc(STR_LEN); - BUILD_MATRIX_FILE_PATH(entry, "reported/", "_spmv_x_1.out", C_str, sigma_str, pathToReportedData); - FILE *reportedData = fopen(pathToReportedData, "w"); - - printf("pathToReportedData = %s\n", pathToReportedData); - - dumpVectorToFile(y, A.nr, reportedData); - fclose(reportedData); - - // If the expect and reported data differ in some way - if(diff_files(pathToExpectedData, pathToReportedData)){ - free(pathToReportedData); - free(pathToExpectedData); - free(pathToMatrix); - - closedir(dir); - return 1; - } - } - free(pathToExpectedData); - free(pathToMatrix); - } - } - - closedir(dir); - - if(!validFileCount){ - fprintf(stderr, "No valid files found in %s\n", pathToMatrices); - free(pathToMatrices); - return 1; - } - else{ - free(pathToMatrices); - return 0; - } -} \ No newline at end of file +/** + * Runs SpMV tests for all `.mtx` files in `dataDir/testMatrices/`. + * + * For each matrix: + * 1. Loads the file as a Matrix Market input + * 2. Builds internal format with given C/sigma + * 3. Computes y = A * x with x = 1 + * 4. Dumps the result to a file + * 5. Compares against expected result (if present) + * + * Returns 0 on success, 1 if any test fails. + */ + +int test_spmvSCS(void* args, const char* dataDir) +{ + // Compose path to directory containing test matrices + char matricesPath[STR_LEN]; + snprintf(matricesPath, STR_LEN, "%s%s", dataDir, "testMatrices/"); + + // Open the matrix directory for reading + DIR* dir = opendir(matricesPath); + if (dir == NULL) { + perror("Error opening directory"); + return 1; + } + + struct dirent* entry; + + // Iterate through files in the directory + while ((entry = readdir(dir)) != NULL) { + // Only process files with ".mtx" extension + if (strstr(entry->d_name, ".mtx") != NULL) { + + // Build full path to matrix file + char matrixPath[STR_LEN]; + snprintf(matrixPath, STR_LEN, "%s%s", matricesPath, entry->d_name); + + MMMatrix M; + Args* arguments = (Args*)args; + + // Extract C and sigma values from filename, using helper macro + char C_str[STR_LEN]; + char sigma_str[STR_LEN]; + sprintf(C_str, "%d", arguments->C); + sprintf(sigma_str, "%d", arguments->sigma); + FORMAT_AND_STRIP_VECTOR_FILE(entry); + + // Path to expected output (".in" file) for this matrix + char pathToExpectedData[STR_LEN]; + snprintf(pathToExpectedData, + STR_LEN, + "data/expected/%s_spmv_x_1.in", + entry->d_name); + + // Only test if expected file exists + if (fopen(pathToExpectedData, "r")) { + + // Path to write reported output (".out" file) + char pathToReportedData[STR_LEN]; + snprintf(pathToReportedData, + STR_LEN, + "data/reported/%s_C_%s_sigma_%s_spmv_x_1.out", + entry->d_name, + C_str, + sigma_str); + + // Open output file for writing results + FILE* reportedData = fopen(pathToReportedData, "w"); + if (reportedData == NULL) { + perror("fopen failed for reportedData"); + exit(EXIT_FAILURE); + } + + // Read matrix from Matrix Market file + MMMatrixRead(&M, matrixPath); + + // Declare matrix and communication structs + GMatrix m; + Matrix A; + Comm c; + + // Set single-rank defaults + M.startRow = 0; + M.stopRow = M.nr; + M.totalNr = M.nr; + M.totalNnz = M.nnz; + c.rank = 0; + c.size = 1; + c.logFile = reportedData; + + // Convert to internal formats + matrixConvertfromMM(&M, &m); // MM → GMatrix + A.C = arguments->C; + A.sigma = arguments->sigma; + convertMatrix(&A, &m); // GMatrix → Matrix + + CG_FLOAT* x = (CG_FLOAT*)allocate(ARRAY_ALIGNMENT, + A.nrPadded * sizeof(CG_FLOAT)); + CG_FLOAT* y = (CG_FLOAT*)allocate(ARRAY_ALIGNMENT, + A.nrPadded * sizeof(CG_FLOAT)); + + for (int i = 0; i < A.nrPadded; ++i) { + x[i] = (CG_FLOAT)1.0; + y[i] = (CG_FLOAT)0.0; + } + + spMVM(&A, x, y); + // Output formatted result + commVectorDump(&c, y, A.nr, pathToReportedData); + fclose(reportedData); + + // Diff against expected file — if mismatch, fail the test + // Need to offset the reported data by 1 line + if (diff_files(pathToExpectedData, pathToReportedData, 1)) { + closedir(dir); + return 1; + } + } + } + } + + // Cleanup and exit + closedir(dir); + return 0; +}