From fdf84e05bbab6589916de41209fc2c9de78ddeed Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:04:58 +0200 Subject: [PATCH 01/22] start github pipeline; Changed default C to c17 and added HAVE_C23 flag in config.mk --- .github/workflows/single-platform.yml | 38 +++++++++++++++++++++++++++ config.mk | 1 + mk/include_CLANG.mk | 8 +++++- mk/include_GCC.mk | 8 +++++- mk/include_ICC.mk | 8 +++++- tests/Makefile | 8 +++++- 6 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/single-platform.yml diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml new file mode 100644 index 0000000..f901b66 --- /dev/null +++ b/.github/workflows/single-platform.yml @@ -0,0 +1,38 @@ +name: Build and Run Unit Tests + +on: + push: + branches: [ "main", "dane_dev" ] + pull_request: + branches: [ "main", "dane_dev" ] + +jobs: + build_ICC_MPI: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Setup Spack + uses: spack/actions/setup-spack + with: + spack-version: latest + + - name: Install OpenMPI + run: | + spack concretize openmpi + spack install -j openmpi + spack load openmpi + + - name: Configure GCC_MPI + run: | + sed -E -i \ + -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ + -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1GCC/' \ + Makefile + + - name: Make GCC_MPI + run: | + make + diff --git a/config.mk b/config.mk index ff941e9..ffbdb6c 100644 --- a/config.mk +++ b/config.mk @@ -4,6 +4,7 @@ TOOLCHAIN ?= ICC MTX_FMT ?= CRS ENABLE_MPI ?= true ENABLE_OPENMP ?= false +HAVE_C23 ?= false FLOAT_TYPE ?= DP # SP for float, DP for double UINT_TYPE ?= U # U for unsigned int, ULL for unsigned long long int diff --git a/mk/include_CLANG.mk b/mk/include_CLANG.mk index e58271e..afd53fe 100644 --- a/mk/include_CLANG.mk +++ b/mk/include_CLANG.mk @@ -12,8 +12,14 @@ OPENMP = -fopenmp #OPENMP = -Xpreprocessor -fopenmp #required on Macos with homebrew libomp endif +# Set default +C_VERSION = c17 +ifeq ($(strip $(HAVE_C23)),true) +C_VERSION = c23 +endif + 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..775af22 100644 --- a/mk/include_GCC.mk +++ b/mk/include_GCC.mk @@ -11,8 +11,14 @@ ifeq ($(strip $(ENABLE_OPENMP)),true) OPENMP = -fopenmp endif +# Set default +C_VERSION = c17 +ifeq ($(strip $(HAVE_C23)),true) +C_VERSION = c23 +endif + 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..31c99e2 100644 --- a/mk/include_ICC.mk +++ b/mk/include_ICC.mk @@ -11,8 +11,14 @@ ifeq ($(strip $(ENABLE_OPENMP)),true) OPENMP = -qopenmp endif +# Set default +C_VERSION = c17 +ifeq ($(strip $(HAVE_C23)),true) +C_VERSION = c23 +endif + 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/tests/Makefile b/tests/Makefile index 8919686..e53dc34 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -23,7 +23,13 @@ MOD2_OBJECTS=${MOD2}/spmvSCS.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 + +# 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 From 7ce286a370ceb0297cf48df59c00ae5bd6171f53 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:08:40 +0200 Subject: [PATCH 02/22] add tag to spack in github workflow --- .github/workflows/single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index f901b66..2fc5ecc 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Spack - uses: spack/actions/setup-spack + uses: spack/actions/setup-spack@main with: spack-version: latest From fff4312171d2a115c9998164ab110be2fd45d214 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:11:25 +0200 Subject: [PATCH 03/22] again fix github workflow --- .github/workflows/single-platform.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 2fc5ecc..09af5e2 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -13,15 +13,18 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Setup Spack - uses: spack/actions/setup-spack@main + - name: Set up Spack + uses: spack/setup-spack@v2 with: - spack-version: latest + ref: develop # Spack version (examples: develop, releases/v0.23) + buildcache: true # Configure oci://ghcr.io/spack/github-actions-buildcache + color: true # Force color output (SPACK_COLOR=always) + path: spack # Where to clone Spack - name: Install OpenMPI run: | spack concretize openmpi - spack install -j openmpi + spack install -j 4 openmpi spack load openmpi - name: Configure GCC_MPI From 8cbac3b651784168990da86426d3cbacf534d673 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:13:14 +0200 Subject: [PATCH 04/22] again again fix github workflow --- .github/workflows/single-platform.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 09af5e2..9b6a4bd 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -23,7 +23,6 @@ jobs: - name: Install OpenMPI run: | - spack concretize openmpi spack install -j 4 openmpi spack load openmpi From 88f8dfa88102940c9b097927b57208550152a3b2 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:16:33 +0200 Subject: [PATCH 05/22] again again again fix github workflow --- .github/workflows/single-platform.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 9b6a4bd..562e38a 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -24,7 +24,8 @@ jobs: - name: Install OpenMPI run: | spack install -j 4 openmpi - spack load openmpi + eval "$(spack load --sh openmpi)" + which mpicc - name: Configure GCC_MPI run: | From f1d329b13ee4e43ac7a5f304100d4883f58a040b Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:23:44 +0200 Subject: [PATCH 06/22] again again again again fix github workflow --- .github/workflows/single-platform.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 562e38a..6d1e5b3 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -27,15 +27,11 @@ jobs: eval "$(spack load --sh openmpi)" which mpicc - - name: Configure GCC_MPI + - name: Configure and Make GCC_MPI run: | sed -E -i \ -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1GCC/' \ - Makefile - - - name: Make GCC_MPI - run: | + -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1GCC/' config.mk make From 2afe1256dda1127b0b28b3364bb2c4031dc0918d Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:31:58 +0200 Subject: [PATCH 07/22] fix github workflow --- .github/workflows/single-platform.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 6d1e5b3..9a180bd 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -24,11 +24,11 @@ jobs: - name: Install OpenMPI run: | spack install -j 4 openmpi - eval "$(spack load --sh openmpi)" - which mpicc - name: Configure and Make GCC_MPI run: | + . ./spack/share/spack/setup-env.sh # setup spack shell env + eval "$(spack load --sh openmpi)" # load into current shell sed -E -i \ -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ From f36470db39ff3d5909962884fb895248dc9f3774 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:38:48 +0200 Subject: [PATCH 08/22] test out ICC github workflow --- .github/workflows/single-platform.yml | 42 +++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 9a180bd..0eaab92 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -7,6 +7,34 @@ on: branches: [ "main", "dane_dev" ] jobs: + # build_GCC_MPI: + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v4 + + # - name: Set up Spack + # uses: spack/setup-spack@v2 + # with: + # ref: develop # Spack version (examples: develop, releases/v0.23) + # buildcache: true # Configure oci://ghcr.io/spack/github-actions-buildcache + # color: true # Force color output (SPACK_COLOR=always) + # path: spack # Where to clone Spack + + # - name: Install OpenMPI + # run: | + # spack install -j 4 openmpi + + # - name: Configure and Make GCC_MPI + # run: | + # . ./spack/share/spack/setup-env.sh # setup spack shell env + # eval "$(spack load --sh openmpi)" # load into current shell + # sed -E -i \ + # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ + # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + # -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1GCC/' config.mk + # make + build_ICC_MPI: runs-on: ubuntu-latest @@ -21,17 +49,19 @@ jobs: color: true # Force color output (SPACK_COLOR=always) path: spack # Where to clone Spack - - name: Install OpenMPI + - name: Install Intel compiler/MPI run: | - spack install -j 4 openmpi + spack install -j 4 intel-oneapi-compilers + spack install -j 4 intel-oneapi-mpi - - name: Configure and Make GCC_MPI + - name: Configure and Make ICC_MPI run: | - . ./spack/share/spack/setup-env.sh # setup spack shell env - eval "$(spack load --sh openmpi)" # load into current shell + . ./spack/share/spack/setup-env.sh + eval "$(spack load --sh intel-oneapi-compilers)" + eval "$(spack load --sh intel-oneapi-mpi)" sed -E -i \ -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1GCC/' config.mk + -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1ICC/' config.mk make From 623c4a41bfc7b9a799fcecfd19ea4c4ee41e96a9 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:48:28 +0200 Subject: [PATCH 09/22] test out CLANG github workflow --- .github/workflows/single-platform.yml | 47 ++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 0eaab92..7ac0ebd 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -35,7 +35,37 @@ jobs: # -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1GCC/' config.mk # make - build_ICC_MPI: + # build_ICC_MPI: + # runs-on: ubuntu-latest + + # steps: + # - uses: actions/checkout@v4 + + # - name: Set up Spack + # uses: spack/setup-spack@v2 + # with: + # ref: develop # Spack version (examples: develop, releases/v0.23) + # buildcache: true # Configure oci://ghcr.io/spack/github-actions-buildcache + # color: true # Force color output (SPACK_COLOR=always) + # path: spack # Where to clone Spack + + # - name: Install Intel compiler/MPI + # run: | + # spack install -j 4 intel-oneapi-compilers + # spack install -j 4 intel-oneapi-mpi + + # - name: Configure and Make ICC_MPI + # run: | + # . ./spack/share/spack/setup-env.sh + # eval "$(spack load --sh intel-oneapi-compilers)" + # eval "$(spack load --sh intel-oneapi-mpi)" + # sed -E -i \ + # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ + # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + # -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1ICC/' config.mk + # make + + build_CLANG_MPI: runs-on: ubuntu-latest steps: @@ -49,19 +79,18 @@ jobs: color: true # Force color output (SPACK_COLOR=always) path: spack # Where to clone Spack - - name: Install Intel compiler/MPI + - name: Install Clang compiler and OpenMPI run: | - spack install -j 4 intel-oneapi-compilers - spack install -j 4 intel-oneapi-mpi + spack install -j 4 llvm + spack install -j 4 spack install -j 4 openmpi - - name: Configure and Make ICC_MPI + - name: Configure and Make CLANG_MPI run: | . ./spack/share/spack/setup-env.sh - eval "$(spack load --sh intel-oneapi-compilers)" - eval "$(spack load --sh intel-oneapi-mpi)" + eval "$(spack load --sh llvm)" + eval "$(spack load --sh openmpi)" sed -E -i \ -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1ICC/' config.mk + -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1CLANG/' config.mk make - From d20805c688589aee72082040f59c4bf74e1ad289 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:51:05 +0200 Subject: [PATCH 10/22] CLANG github workflow typo --- .github/workflows/single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 7ac0ebd..ee34eb1 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -82,7 +82,7 @@ jobs: - name: Install Clang compiler and OpenMPI run: | spack install -j 4 llvm - spack install -j 4 spack install -j 4 openmpi + spack install -j 4 openmpi - name: Configure and Make CLANG_MPI run: | From 06c457ba2a8cf61af8f5e7a8a3aaf423f65354b2 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Fri, 20 Jun 2025 11:56:53 +0200 Subject: [PATCH 11/22] Collect all MPI builds intto a single parallel job --- .github/workflows/single-platform.yml | 111 +++++++++----------------- 1 file changed, 39 insertions(+), 72 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index ee34eb1..5246bab 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -7,66 +7,11 @@ on: branches: [ "main", "dane_dev" ] jobs: - # build_GCC_MPI: - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v4 - - # - name: Set up Spack - # uses: spack/setup-spack@v2 - # with: - # ref: develop # Spack version (examples: develop, releases/v0.23) - # buildcache: true # Configure oci://ghcr.io/spack/github-actions-buildcache - # color: true # Force color output (SPACK_COLOR=always) - # path: spack # Where to clone Spack - - # - name: Install OpenMPI - # run: | - # spack install -j 4 openmpi - - # - name: Configure and Make GCC_MPI - # run: | - # . ./spack/share/spack/setup-env.sh # setup spack shell env - # eval "$(spack load --sh openmpi)" # load into current shell - # sed -E -i \ - # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ - # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - # -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1GCC/' config.mk - # make - - # build_ICC_MPI: - # runs-on: ubuntu-latest - - # steps: - # - uses: actions/checkout@v4 - - # - name: Set up Spack - # uses: spack/setup-spack@v2 - # with: - # ref: develop # Spack version (examples: develop, releases/v0.23) - # buildcache: true # Configure oci://ghcr.io/spack/github-actions-buildcache - # color: true # Force color output (SPACK_COLOR=always) - # path: spack # Where to clone Spack - - # - name: Install Intel compiler/MPI - # run: | - # spack install -j 4 intel-oneapi-compilers - # spack install -j 4 intel-oneapi-mpi - - # - name: Configure and Make ICC_MPI - # run: | - # . ./spack/share/spack/setup-env.sh - # eval "$(spack load --sh intel-oneapi-compilers)" - # eval "$(spack load --sh intel-oneapi-mpi)" - # sed -E -i \ - # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ - # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - # -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1ICC/' config.mk - # make - - build_CLANG_MPI: + build: runs-on: ubuntu-latest + strategy: + matrix: + compiler: [GCC, ICC, CLANG] steps: - uses: actions/checkout@v4 @@ -74,23 +19,45 @@ jobs: - name: Set up Spack uses: spack/setup-spack@v2 with: - ref: develop # Spack version (examples: develop, releases/v0.23) - buildcache: true # Configure oci://ghcr.io/spack/github-actions-buildcache - color: true # Force color output (SPACK_COLOR=always) - path: spack # Where to clone Spack + ref: develop + buildcache: true + color: true + path: spack - - name: Install Clang compiler and OpenMPI + - name: Install Compiler and MPI run: | - spack install -j 4 llvm - spack install -j 4 openmpi + . ./spack/share/spack/setup-env.sh - - name: Configure and Make CLANG_MPI + if [[ "${{ matrix.compiler }}" == "GCC" ]]; then + spack install -j 4 openmpi + elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then + spack install -j 4 intel-oneapi-compilers + spack install -j 4 intel-oneapi-mpi + elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then + spack install -j 4 llvm + spack install -j 4 openmpi + fi + + - name: Configure and Make run: | . ./spack/share/spack/setup-env.sh - eval "$(spack load --sh llvm)" - eval "$(spack load --sh openmpi)" + + if [[ "${{ matrix.compiler }}" == "GCC" ]]; then + eval "$(spack load --sh openmpi)" + elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then + eval "$(spack load --sh intel-oneapi-compilers)" + eval "$(spack load --sh intel-oneapi-mpi)" + elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then + eval "$(spack load --sh llvm)" + eval "$(spack load --sh openmpi)" + fi + + TOOLCHAIN="${{ matrix.compiler }}" + sed -E -i \ - -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ - -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - -e 's/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1CLANG/' config.mk + -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ + -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ + config.mk + make From fb91332f33cf5fa6a2da02d11ef49c224cf485ad Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Tue, 24 Jun 2025 20:42:56 +0200 Subject: [PATCH 12/22] update tests; force SCS build in CI so tests work; change SCS dump routine to use fprintf --- .github/workflows/single-platform.yml | 37 ++++ src/comm.c | 59 +++--- src/matrix-SCS.c | 4 +- tests/Makefile | 4 +- tests/common.h | 185 +++++++++--------- tests/matrix/convertSCS.c | 204 ++++++++++++-------- tests/runTests.c | 11 +- tests/solver/spmvSCS.c | 268 ++++++++++++++------------ 8 files changed, 433 insertions(+), 339 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 5246bab..0f1d004 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -57,7 +57,44 @@ jobs: sed -E -i \ -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\SCS/' \ -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ config.mk make + + - name: List exes after build + run: | + echo "Current directory: $(pwd)" + echo "Contents:" + find . -type f -executable + + - name: Upload Executables + uses: actions/upload-artifact@v4 + with: + name: executable-${{ matrix.compiler }} + path: | + $(pwd)/sparseBench-SCS-${{ matrix.compiler }} + + # runTests: + # runs-on: ubuntu-latest + # needs: build + # strategy: + # matrix: + # compiler: [GCC, ICC, CLANG] + + # steps: + # - uses: actions/checkout@v4 + + # - name: Download Executables + # uses: actions/download-artifact@v4 + # with: + # name: executable-${{ matrix.compiler }} + # path: ./executables + + # - name: Run Tests + # run: | + # chmod +x ./executables/* + # ./executables/executable1 --test + # ./executables/executable2 --test + # ./executables/executable3 --test diff --git a/src/comm.c b/src/comm.c index 574e595..42d58bd 100644 --- a/src/comm.c +++ b/src/comm.c @@ -753,53 +753,54 @@ void commMatrixDump(Comm* c, Matrix* m) } #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 */ } 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 e53dc34..229a2cf 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -5,7 +5,7 @@ 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 @@ -23,7 +23,7 @@ MOD2_OBJECTS=${MOD2}/spmvSCS.o ${MOD2}/solverTests.o OBJECTS := $(shell echo $(MOD1_OBJECTS) $(MOD2_OBJECTS) | tr ' ' '\n' | sort -u | tr '\n' ' ') # Always leave debugging flag on -CFLAGS=-g +CFLAGS=-g $(DEFINES) # Only enable OpenMP if it defined in the parent config.mk ifeq ($(strip $(ENABLE_OPENMP)),true) diff --git a/tests/common.h b/tests/common.h index 0d65e27..bb6f0f8 100644 --- a/tests/common.h +++ b/tests/common.h @@ -4,68 +4,68 @@ #include #include -#define SET_ARGS(i, C_val, sigma_val) \ -{ \ - args[i]->C = (C_val); \ - args[i]->sigma = (sigma_val); \ -} +#define SET_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'; \ - } \ -} +#define FORMAT_AND_STRIP_MATRIX_FILE(A, entry, C_str, sigma_str, arguments) \ + { \ + sprintf((C_str), "%d", arguments->C); \ + sprintf((sigma_str), "%d", arguments->sigma); \ + 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 +77,57 @@ 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) +{ + 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 } #endif //__COMMON_H_ diff --git a/tests/matrix/convertSCS.c b/tests/matrix/convertSCS.c index ce391c0..a17b402 100644 --- a/tests/matrix/convertSCS.c +++ b/tests/matrix/convertSCS.c @@ -1,91 +1,129 @@ // DL 2025.04.04 // Single rank test to convert MM to SCS format +// We force the matrix format to be Sell-C-sigma on a single rank +#ifdef _MPI +#undef _MPI +#endif +#ifdef CRS +#undef CRS +#endif +#ifdef CCRS +#undef CCRS +#endif + +#define SCS + +#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; +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); + + MMMatrix M; + Args* arguments = (Args*)args; + + // String preprocessing + char C_str[STR_LEN]; + char sigma_str[STR_LEN]; + FORMAT_AND_STRIP_MATRIX_FILE(M, entry, C_str, sigma_str, arguments) + + // 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")) { + + // 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"); + if (reportedData == NULL) { + perror("fopen failed for reportedData"); + exit(EXIT_FAILURE); + } + + MMMatrixRead(&M, pathToMatrix); + + 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; + + matrixConvertfromMM(&M, &m); + + A.C = arguments->C; + A.sigma = arguments->sigma; + + convertMatrix(&A, &m); + commMatrixDump(&c, &A); + 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 diff --git a/tests/runTests.c b/tests/runTests.c index ec7c6d5..9db5d0a 100644 --- a/tests/runTests.c +++ b/tests/runTests.c @@ -6,9 +6,10 @@ #include "matrix/matrixTests.h" #include "solver/solverTests.h" -int main(int argc, char** argv){ - matrixTests(argc, argv); - solverTests(argc, argv); - - return 0; +int main(int argc, char** argv) +{ + matrixTests(argc, argv); + // solverTests(argc, argv); + + return 0; } \ No newline at end of file diff --git a/tests/solver/spmvSCS.c b/tests/solver/spmvSCS.c index 48dfd73..76e54d9 100644 --- a/tests/solver/spmvSCS.c +++ b/tests/solver/spmvSCS.c @@ -1,140 +1,154 @@ // DL 2025.04.07 // Single rank SpMV test -#include -#include -#include -#include +// We force the matrix format to be Sell-C-sigma on a single rank +#ifdef _MPI +#undef _MPI +#endif +#ifdef CRS +#undef CRS +#endif +#ifdef CCRS +#undef CCRS +#endif + +#define SCS + +#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; - } +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); + + MMMatrix M; + Args* arguments = (Args*)args; + + char C_str[STR_LEN]; + char sigma_str[STR_LEN]; + sprintf(C_str, "%d", arguments->C); + sprintf(sigma_str, "%d", arguments->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); + + if (fopen(pathToExpectedData, "r")) { + ++validFileCount; + + MMMatrix M; + MMMatrixRead(&M, pathToMatrix); + + // 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"); + if (reportedData == NULL) { + perror("fopen failed for reportedData"); + exit(EXIT_FAILURE); + } + + 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; + + matrixConvertfromMM(&M, &m); + convertMatrix(&A, &m); + + 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)); + + // Fix x = 1 for now + for (int i = 0; i < A.nrPadded; ++i) { + x[i] = (CG_FLOAT)1.0; + y[i] = (CG_FLOAT)0.0; + } + + spMVM(&A, x, y); + + commVectorDump(&c, y, A.nr, pathToReportedData); + 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 From 977bf1e43cce0f8f3a04e10e2405fa7eb9ff4b21 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Tue, 24 Jun 2025 20:48:51 +0200 Subject: [PATCH 13/22] try pipeline again --- .github/workflows/single-platform.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 0f1d004..c6c2ee2 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -57,7 +57,7 @@ jobs: sed -E -i \ -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\SCS/' \ + -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\1SCS/' \ -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ config.mk From 9ae59167f59fa5cb0fbe9b092ee871f2d37b908b Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Tue, 24 Jun 2025 20:56:47 +0200 Subject: [PATCH 14/22] try pipeline again --- .github/workflows/single-platform.yml | 58 +++++++++++++-------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index c6c2ee2..2691c73 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -63,38 +63,34 @@ jobs: make - - name: List exes after build - run: | - echo "Current directory: $(pwd)" - echo "Contents:" - find . -type f -executable - - name: Upload Executables uses: actions/upload-artifact@v4 with: - name: executable-${{ matrix.compiler }} + name: sparseBench-SCS-${{ matrix.compiler }} path: | - $(pwd)/sparseBench-SCS-${{ matrix.compiler }} - - # runTests: - # runs-on: ubuntu-latest - # needs: build - # strategy: - # matrix: - # compiler: [GCC, ICC, CLANG] - - # steps: - # - uses: actions/checkout@v4 - - # - name: Download Executables - # uses: actions/download-artifact@v4 - # with: - # name: executable-${{ matrix.compiler }} - # path: ./executables - - # - name: Run Tests - # run: | - # chmod +x ./executables/* - # ./executables/executable1 --test - # ./executables/executable2 --test - # ./executables/executable3 --test + ./sparseBench-SCS-${{ matrix.compiler }} + + runTests: + runs-on: ubuntu-latest + needs: build + strategy: + matrix: + compiler: [GCC, ICC, CLANG] + + steps: + - uses: actions/checkout@v4 + + - name: Download Executables + uses: actions/download-artifact@v4 + with: + name: sparseBench-SCS-${{ matrix.compiler }} + path: ./executables + + - name: Make Tests + run: | + cd tests + make + + - name: Run Tests + run: | + ./runTests From ec318b190747db388a4771c3089bfc44c5ab4b8a Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Tue, 24 Jun 2025 21:08:52 +0200 Subject: [PATCH 15/22] try pipeline again --- .github/workflows/single-platform.yml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 2691c73..4374d25 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -41,7 +41,7 @@ jobs: - name: Configure and Make run: | . ./spack/share/spack/setup-env.sh - + if [[ "${{ matrix.compiler }}" == "GCC" ]]; then eval "$(spack load --sh openmpi)" elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then @@ -86,6 +86,28 @@ jobs: name: sparseBench-SCS-${{ matrix.compiler }} path: ./executables + - name: Set up Spack + uses: spack/setup-spack@v2 + with: + ref: develop + buildcache: true + color: true + path: spack + + - name: Load Compiler and MPI + run: | + . ./spack/share/spack/setup-env.sh + + if [[ "${{ matrix.compiler }}" == "GCC" ]]; then + eval "$(spack load --sh openmpi)" + elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then + eval "$(spack load --sh intel-oneapi-compilers)" + eval "$(spack load --sh intel-oneapi-mpi)" + elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then + eval "$(spack load --sh llvm)" + eval "$(spack load --sh openmpi)" + fi + - name: Make Tests run: | cd tests From 70d2c03616cae3bcfed2b00fd3bdda3f976a17aa Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Wed, 25 Jun 2025 08:16:05 +0200 Subject: [PATCH 16/22] try pipeline again --- .github/workflows/single-platform.yml | 101 ++++++++++++++------------ 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 4374d25..f12c2f4 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -7,7 +7,7 @@ on: branches: [ "main", "dane_dev" ] jobs: - build: + testMPI: runs-on: ubuntu-latest strategy: matrix: @@ -63,51 +63,6 @@ jobs: make - - name: Upload Executables - uses: actions/upload-artifact@v4 - with: - name: sparseBench-SCS-${{ matrix.compiler }} - path: | - ./sparseBench-SCS-${{ matrix.compiler }} - - runTests: - runs-on: ubuntu-latest - needs: build - strategy: - matrix: - compiler: [GCC, ICC, CLANG] - - steps: - - uses: actions/checkout@v4 - - - name: Download Executables - uses: actions/download-artifact@v4 - with: - name: sparseBench-SCS-${{ matrix.compiler }} - path: ./executables - - - name: Set up Spack - uses: spack/setup-spack@v2 - with: - ref: develop - buildcache: true - color: true - path: spack - - - name: Load Compiler and MPI - run: | - . ./spack/share/spack/setup-env.sh - - if [[ "${{ matrix.compiler }}" == "GCC" ]]; then - eval "$(spack load --sh openmpi)" - elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then - eval "$(spack load --sh intel-oneapi-compilers)" - eval "$(spack load --sh intel-oneapi-mpi)" - elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then - eval "$(spack load --sh llvm)" - eval "$(spack load --sh openmpi)" - fi - - name: Make Tests run: | cd tests @@ -116,3 +71,57 @@ jobs: - name: Run Tests run: | ./runTests + + # - name: Upload Executables + # uses: actions/upload-artifact@v4 + # with: + # name: sparseBench-SCS-${{ matrix.compiler }} + # path: | + # ./sparseBench-SCS-${{ matrix.compiler }} + + # runTests: + # runs-on: ubuntu-latest + # needs: build + # strategy: + # matrix: + # compiler: [GCC, ICC, CLANG] + + # steps: + # - uses: actions/checkout@v4 + + # - name: Download Executables + # uses: actions/download-artifact@v4 + # with: + # name: sparseBench-SCS-${{ matrix.compiler }} + # path: ./executables + + # - name: Set up Spack + # uses: spack/setup-spack@v2 + # with: + # ref: develop + # buildcache: true + # color: true + # path: spack + + # - name: Load Compiler and MPI + # run: | + # . ./spack/share/spack/setup-env.sh + + # if [[ "${{ matrix.compiler }}" == "GCC" ]]; then + # eval "$(spack load --sh openmpi)" + # elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then + # eval "$(spack load --sh intel-oneapi-compilers)" + # eval "$(spack load --sh intel-oneapi-mpi)" + # elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then + # eval "$(spack load --sh llvm)" + # eval "$(spack load --sh openmpi)" + # fi + + # - name: Make Tests + # run: | + # cd tests + # make + + # - name: Run Tests + # run: | + # ./runTests From c2c24e481a33e4158d03b3b173690239b8873b38 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Wed, 25 Jun 2025 08:27:00 +0200 Subject: [PATCH 17/22] try pipeline again --- .github/workflows/single-platform.yml | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index f12c2f4..2fc0a32 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -16,7 +16,7 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Spack + - name: Set-up Spack uses: spack/setup-spack@v2 with: ref: develop @@ -24,7 +24,7 @@ jobs: color: true path: spack - - name: Install Compiler and MPI + - name: Install Compilers and MPI Wrappers run: | . ./spack/share/spack/setup-env.sh @@ -38,7 +38,7 @@ jobs: spack install -j 4 openmpi fi - - name: Configure and Make + - name: Configure and Make SparseBench run: | . ./spack/share/spack/setup-env.sh @@ -63,15 +63,28 @@ jobs: make - - name: Make Tests + - name: Make and Run Tests run: | + . ./spack/share/spack/setup-env.sh + + if [[ "${{ matrix.compiler }}" == "GCC" ]]; then + eval "$(spack load --sh openmpi)" + elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then + eval "$(spack load --sh intel-oneapi-compilers)" + eval "$(spack load --sh intel-oneapi-mpi)" + elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then + eval "$(spack load --sh llvm)" + eval "$(spack load --sh openmpi)" + fi + cd tests make - - - name: Run Tests - run: | ./runTests + # - name: Run Tests + # run: | + # ./runTests + # - name: Upload Executables # uses: actions/upload-artifact@v4 # with: From 233abccccf1cf2e261345140c2ea808096c386e1 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Wed, 25 Jun 2025 08:37:13 +0200 Subject: [PATCH 18/22] add empty dir for reported test data --- .gitignore | 1 + tests/data/reported/.gitignore | 0 2 files changed, 1 insertion(+) create mode 100644 tests/data/reported/.gitignore 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/tests/data/reported/.gitignore b/tests/data/reported/.gitignore new file mode 100644 index 0000000..e69de29 From 7fa4bff624c56372284a68f2c8abab5432638ca8 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Wed, 25 Jun 2025 09:06:18 +0200 Subject: [PATCH 19/22] Finalize github workflow and add comments --- .github/workflows/single-platform.yml | 150 +++++++++----------------- 1 file changed, 51 insertions(+), 99 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index 2fc0a32..efb0b09 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -9,51 +9,71 @@ on: jobs: testMPI: runs-on: ubuntu-latest + # Define a build matrix over compilers strategy: matrix: compiler: [GCC, ICC, CLANG] steps: - - uses: actions/checkout@v4 + # 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 - buildcache: true - color: true - path: spack + 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 - if [[ "${{ matrix.compiler }}" == "GCC" ]]; then - spack install -j 4 openmpi - elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then - spack install -j 4 intel-oneapi-compilers - spack install -j 4 intel-oneapi-mpi - elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then - spack install -j 4 llvm - spack install -j 4 openmpi - fi - - - name: Configure and Make SparseBench + # 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 - if [[ "${{ matrix.compiler }}" == "GCC" ]]; then - eval "$(spack load --sh openmpi)" - elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then - eval "$(spack load --sh intel-oneapi-compilers)" - eval "$(spack load --sh intel-oneapi-mpi)" - elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then - eval "$(spack load --sh llvm)" - eval "$(spack load --sh openmpi)" - fi - + # 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 TOOLCHAIN="${{ matrix.compiler }}" + # Update build configuration flags in config.mk sed -E -i \ -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ @@ -61,80 +81,12 @@ jobs: -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ config.mk + # Build the main project make - - name: Make and Run Tests - run: | - . ./spack/share/spack/setup-env.sh - - if [[ "${{ matrix.compiler }}" == "GCC" ]]; then - eval "$(spack load --sh openmpi)" - elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then - eval "$(spack load --sh intel-oneapi-compilers)" - eval "$(spack load --sh intel-oneapi-mpi)" - elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then - eval "$(spack load --sh llvm)" - eval "$(spack load --sh openmpi)" - fi - + # Move into the tests directory cd tests + # Build test suite make - ./runTests - - # - name: Run Tests - # run: | - # ./runTests - - # - name: Upload Executables - # uses: actions/upload-artifact@v4 - # with: - # name: sparseBench-SCS-${{ matrix.compiler }} - # path: | - # ./sparseBench-SCS-${{ matrix.compiler }} - - # runTests: - # runs-on: ubuntu-latest - # needs: build - # strategy: - # matrix: - # compiler: [GCC, ICC, CLANG] - - # steps: - # - uses: actions/checkout@v4 - - # - name: Download Executables - # uses: actions/download-artifact@v4 - # with: - # name: sparseBench-SCS-${{ matrix.compiler }} - # path: ./executables - - # - name: Set up Spack - # uses: spack/setup-spack@v2 - # with: - # ref: develop - # buildcache: true - # color: true - # path: spack - - # - name: Load Compiler and MPI - # run: | - # . ./spack/share/spack/setup-env.sh - - # if [[ "${{ matrix.compiler }}" == "GCC" ]]; then - # eval "$(spack load --sh openmpi)" - # elif [[ "${{ matrix.compiler }}" == "ICC" ]]; then - # eval "$(spack load --sh intel-oneapi-compilers)" - # eval "$(spack load --sh intel-oneapi-mpi)" - # elif [[ "${{ matrix.compiler }}" == "CLANG" ]]; then - # eval "$(spack load --sh llvm)" - # eval "$(spack load --sh openmpi)" - # fi - - # - name: Make Tests - # run: | - # cd tests - # make - - # - name: Run Tests - # run: | - # ./runTests + # Run the tests + ./runTests \ No newline at end of file From d7b1d0a11cabe186f80f05d57afadab944c44b5e Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Wed, 25 Jun 2025 13:10:01 +0200 Subject: [PATCH 20/22] clean up string handling in unit tests; built tests based on MTX_FMT in config.mk; make MTX_FMT flexible in github test runner --- .github/workflows/single-platform.yml | 70 +++++++++---- src/comm.c | 20 ++-- tests/Makefile | 12 +-- tests/common.h | 28 ++++-- tests/data/expected/test0_CRS.in | 12 +++ tests/data/expected/test0_spmv_x_1.in | 11 ++- tests/data/expected/test8_CRS.in | 12 +++ tests/data/expected/test8_spmv_x_1.in | 10 ++ tests/matrix/convertCRS.c | 114 +++++++++++++++++++++ tests/matrix/convertCRS.h | 6 ++ tests/matrix/convertSCS.c | 128 ++++++++++++------------ tests/matrix/matrixTests.c | 111 ++++++++++----------- tests/runTests.c | 15 ++- tests/solver/solverTests.c | 113 ++++++++++----------- tests/solver/spmvCRS.c | 136 ++++++++++++++++++++++++++ tests/solver/spmvCRS.h | 6 ++ tests/solver/spmvSCS.c | 136 ++++++++++++-------------- 17 files changed, 636 insertions(+), 304 deletions(-) create mode 100644 tests/data/expected/test0_CRS.in create mode 100644 tests/data/expected/test8_CRS.in create mode 100644 tests/data/expected/test8_spmv_x_1.in create mode 100644 tests/matrix/convertCRS.c create mode 100644 tests/matrix/convertCRS.h create mode 100644 tests/solver/spmvCRS.c create mode 100644 tests/solver/spmvCRS.h diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index efb0b09..caa6d9e 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -71,22 +71,54 @@ jobs: esac # Export TOOLCHAIN for the Makefile - TOOLCHAIN="${{ matrix.compiler }}" - - # Update build configuration flags in config.mk - sed -E -i \ - -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ - -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\1SCS/' \ - -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ - config.mk - - # Build the main project - make - - # Move into the tests directory - cd tests - # Build test suite - make - # Run the tests - ./runTests \ No newline at end of file + 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:]]*\?=).*/\1true/' \ + -e 's/^(ENABLE_OPENMP[[:space:]]*\?=).*/\1false/' \ + -e "s/^(MTX_FMT[[:space:]]*\?=).*/\1$FMT/" \ + -e "s/^(TOOLCHAIN[[:space:]]*\?=).*/\1${{ matrix.compiler }}/" \ + config.mk + + make -j # Build sparseBench + cd tests + make -j # Build tests + mpirun -n 1 ./runTests # Run (single rank) tests + cd .. + } + + # Run both test formats + run_tests CRS + run_tests SCS + + # # Set build configuration flags to CRS in config.mk + # sed -E -i \ + # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ + # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + # -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\1CRS/' \ + # -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ + # config.mk + + # make # Build the main project + # cd tests # Move into the tests directory + # make # Build test suite + # mpirun -n 1 ./runTests # Run the (single rank) tests + + # cd ../ + # # Set build configuration flags to SCS in config.mk + # sed -E -i \ + # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ + # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ + # -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\1SCS/' \ + # -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ + # config.mk + + # make # Build the main project + # cd tests # Move into the tests directory + # make # Build test suite + # mpirun -n 1 ./runTests # Run the (single rank) tests \ No newline at end of file diff --git a/src/comm.c b/src/comm.c index 42d58bd..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,30 +727,28 @@ 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 @@ -802,6 +801,9 @@ void commMatrixDump(Comm* c, Matrix* m) } 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/tests/Makefile b/tests/Makefile index 229a2cf..8c0cef3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,4 +1,4 @@ -# # # DL 2025.04.04 +# # # DL 2025.06.25 # # # Collection of unit tests include ../config.mk @@ -18,8 +18,8 @@ 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 @@ -33,7 +33,7 @@ endif .PHONY: all clean -all: $(TARGET) +all: clean $(TARGET) clean: rm -f $(TARGET) $(OBJECTS) runTests.o @@ -50,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 bb6f0f8..e96ba6b 100644 --- a/tests/common.h +++ b/tests/common.h @@ -4,10 +4,10 @@ #include #include -#define SET_ARGS(i, C_val, sigma_val) \ +#define SET_SCS_ARGS(i, C_val, sigma_val) \ { \ - args[i]->C = (C_val); \ - args[i]->sigma = (sigma_val); \ + args[i].C = (C_val); \ + args[i].sigma = (sigma_val); \ } typedef int (*TestFunc)(void* config, const char* dataDir); @@ -36,11 +36,9 @@ typedef struct { } #endif -#ifndef FORMAT_AND_STRIP_MATRIX_FILE -#define FORMAT_AND_STRIP_MATRIX_FILE(A, entry, C_str, sigma_str, arguments) \ +#ifndef STRIP_MATRIX_FILE +#define STRIP_MATRIX_FILE(entry) \ { \ - sprintf((C_str), "%d", arguments->C); \ - sprintf((sigma_str), "%d", arguments->sigma); \ char* dot = strrchr((entry)->d_name, '.'); \ if (dot != NULL) { \ *dot = '\0'; \ @@ -77,7 +75,8 @@ typedef struct { #define ARRAY_ALIGNMENT 64 #endif -static int diff_files(const char* expectedData, const char* reportedData) +static int diff_files( + const char* expectedData, const char* reportedData, int offset) { FILE* f1 = fopen(expectedData, "r"); FILE* f2 = fopen(reportedData, "r"); @@ -90,6 +89,19 @@ static int diff_files(const char* expectedData, const char* reportedData) 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) { 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/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 a17b402..b6fe21f 100644 --- a/tests/matrix/convertSCS.c +++ b/tests/matrix/convertSCS.c @@ -1,18 +1,7 @@ -// DL 2025.04.04 -// Single rank test to convert MM to SCS format - -// We force the matrix format to be Sell-C-sigma on a single rank -#ifdef _MPI -#undef _MPI -#endif -#ifdef CRS -#undef CRS -#endif -#ifdef CCRS -#undef CCRS -#endif - -#define SCS +// 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" @@ -22,18 +11,26 @@ #include #include +/** + * 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/"); - 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); + // Open the directory and check for errors + DIR* dir = opendir(matricesPath); if (dir == NULL) { perror("Error opening directory"); return 1; @@ -41,54 +38,64 @@ int test_convertSCS(void* args, const char* dataDir) // 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) { - char* pathToMatrix = malloc( - strlen(pathToMatrices) + strlen(entry->d_name) + 1); - strcpy(pathToMatrix, pathToMatrices); - strcat(pathToMatrix, entry->d_name); + + // 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; - // String preprocessing + // Extract C and sigma values from filename, using helper macro char C_str[STR_LEN]; char sigma_str[STR_LEN]; - FORMAT_AND_STRIP_MATRIX_FILE(M, entry, C_str, sigma_str, arguments) + sprintf(C_str, "%d", arguments->C); + sprintf(sigma_str, "%d", arguments->sigma); + + STRIP_MATRIX_FILE(entry) // Removes .mtx from file name - // This is the external file to check against - char* pathToExpectedData = malloc(STR_LEN); - BUILD_MATRIX_FILE_PATH(entry, - "expected/", - ".in", + // 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, - pathToExpectedData); + sigma_str); - // Validate against expected data, if it exists + // Only test if expected file exists if (fopen(pathToExpectedData, "r")) { - // Dump to this external file - char* pathToReportedData = malloc(STR_LEN); - BUILD_MATRIX_FILE_PATH(entry, - "reported/", - ".out", + // 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, - pathToReportedData); + sigma_str); + + // Open output file for writing results FILE* reportedData = fopen(pathToReportedData, "w"); if (reportedData == NULL) { perror("fopen failed for reportedData"); - exit(EXIT_FAILURE); + exit(EXIT_FAILURE); // Crash fast on I/O error } - MMMatrixRead(&M, pathToMatrix); + // Read matrix from Matrix Market file + MMMatrixRead(&M, matrixPath); + // Declare matrix and communication structs GMatrix m; Matrix A; Comm c; - // Set single rank defaults + // Set single-rank defaults M.startRow = 0; M.stopRow = M.nr; M.totalNr = M.nr; @@ -97,33 +104,24 @@ int test_convertSCS(void* args, const char* dataDir) c.size = 1; c.logFile = reportedData; - matrixConvertfromMM(&M, &m); - + // Convert to internal formats + matrixConvertfromMM(&M, &m); // MM → GMatrix A.C = arguments->C; A.sigma = arguments->sigma; - - convertMatrix(&A, &m); - commMatrixDump(&c, &A); + convertMatrix(&A, &m); // GMatrix → Matrix + commMatrixDump(&c, &A); // Output formatted result fclose(reportedData); - // If the expect and reported data differ in some way - if (diff_files(pathToExpectedData, pathToReportedData)) { - free(pathToReportedData); - free(pathToExpectedData); - free(pathToMatrix); - + // Diff against expected file — if mismatch, fail the test + if (diff_files(pathToExpectedData, pathToReportedData, 0)) { closedir(dir); return 1; } } - - free(pathToExpectedData); - free(pathToMatrix); } } - free(pathToMatrices); + // Clean up and return success closedir(dir); - return 0; -} \ No newline at end of file +} 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 9db5d0a..643c68c 100644 --- a/tests/runTests.c +++ b/tests/runTests.c @@ -1,4 +1,3 @@ - #include #include #include @@ -6,10 +5,22 @@ #include "matrix/matrixTests.h" #include "solver/solverTests.h" +#ifdef _MPI +#include +#endif + int main(int argc, char** argv) { +#ifdef _MPI + MPI_Init(&argc, &argv); +#endif + matrixTests(argc, argv); - // solverTests(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 76e54d9..227e638 100644 --- a/tests/solver/spmvSCS.c +++ b/tests/solver/spmvSCS.c @@ -1,18 +1,7 @@ -// DL 2025.04.07 -// Single rank SpMV test - -// We force the matrix format to be Sell-C-sigma on a single rank -#ifdef _MPI -#undef _MPI -#endif -#ifdef CRS -#undef CRS -#endif -#ifdef CCRS -#undef CCRS -#endif - -#define SCS +// 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 "../../src/allocate.h" #include "../../src/comm.h" @@ -30,75 +19,88 @@ #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. 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/"); - 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); + // Open the matrix directory for reading + 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) { - char* pathToMatrix = malloc( - strlen(pathToMatrices) + strlen(entry->d_name) + 1); - strcpy(pathToMatrix, pathToMatrices); - strcat(pathToMatrix, entry->d_name); + + // 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); - // 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); + // 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")) { - ++validFileCount; - - MMMatrix M; - MMMatrixRead(&M, pathToMatrix); - // Dump to this external file - char* pathToReportedData = malloc(STR_LEN); - BUILD_MATRIX_FILE_PATH(entry, - "reported/", - "_spmv_x_1.out", + // 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, - pathToReportedData); + 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 + // Set single-rank defaults M.startRow = 0; M.stopRow = M.nr; M.totalNr = M.nr; @@ -107,48 +109,38 @@ int test_spmvSCS(void* args, const char* dataDir) c.size = 1; c.logFile = reportedData; - matrixConvertfromMM(&M, &m); - convertMatrix(&A, &m); + // 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)); - // Fix x = 1 for now 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); - // If the expect and reported data differ in some way - if (diff_files(pathToExpectedData, pathToReportedData)) { - free(pathToReportedData); - free(pathToExpectedData); - free(pathToMatrix); - + // 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; } } - free(pathToExpectedData); - free(pathToMatrix); } } + // Cleanup and exit 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 + return 0; +} From bbfb40f89f5506771c66fd6ff78a2a1ceda07644 Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Wed, 25 Jun 2025 14:00:00 +0200 Subject: [PATCH 21/22] adjust pipeline logic --- .github/workflows/single-platform.yml | 53 ++++++++------------------- tests/Makefile | 2 +- 2 files changed, 16 insertions(+), 39 deletions(-) diff --git a/.github/workflows/single-platform.yml b/.github/workflows/single-platform.yml index caa6d9e..bab20a3 100644 --- a/.github/workflows/single-platform.yml +++ b/.github/workflows/single-platform.yml @@ -79,46 +79,23 @@ jobs: echo ">>> Building and testing with $FMT matrix format." sed -E -i \ - -e 's/^(ENABLE_MPI[[:space:]]*\?=).*/\1true/' \ - -e 's/^(ENABLE_OPENMP[[:space:]]*\?=).*/\1false/' \ - -e "s/^(MTX_FMT[[:space:]]*\?=).*/\1$FMT/" \ - -e "s/^(TOOLCHAIN[[:space:]]*\?=).*/\1${{ matrix.compiler }}/" \ - config.mk + -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 - make -j # Build sparseBench - cd tests - make -j # Build tests - mpirun -n 1 ./runTests # Run (single rank) tests + # Build (MPI-only) sparseBench + make + + # Build tests + cd tests && make clean && make + + # Run (single rank) tests + mpirun -n 1 ./runTests cd .. } - # Run both test formats + # Run tests with both formats run_tests CRS - run_tests SCS - - # # Set build configuration flags to CRS in config.mk - # sed -E -i \ - # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ - # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - # -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\1CRS/' \ - # -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ - # config.mk - - # make # Build the main project - # cd tests # Move into the tests directory - # make # Build test suite - # mpirun -n 1 ./runTests # Run the (single rank) tests - - # cd ../ - # # Set build configuration flags to SCS in config.mk - # sed -E -i \ - # -e 's/^(ENABLE_MPI[[:space:]]*\?=[[:space:]]*).*/\1true/' \ - # -e 's/^(ENABLE_OPENMP[[:space:]]*\?=[[:space:]]*).*/\1false/' \ - # -e 's/^(MTX_FMT[[:space:]]*\?=[[:space:]]*).*/\1SCS/' \ - # -e "s/^(TOOLCHAIN[[:space:]]*\?=[[:space:]]*).*/\1${TOOLCHAIN}/" \ - # config.mk - - # make # Build the main project - # cd tests # Move into the tests directory - # make # Build test suite - # mpirun -n 1 ./runTests # Run the (single rank) tests \ No newline at end of file + run_tests SCS \ No newline at end of file diff --git a/tests/Makefile b/tests/Makefile index 8c0cef3..596454f 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -33,7 +33,7 @@ endif .PHONY: all clean -all: clean $(TARGET) +all: $(TARGET) clean: rm -f $(TARGET) $(OBJECTS) runTests.o From 5004951f3e0146c75f18b316aca013af02496f6b Mon Sep 17 00:00:00 2001 From: Dane Lacey Date: Sun, 29 Jun 2025 07:52:58 +0200 Subject: [PATCH 22/22] remove HAVE_C23 config.mk option --- config.mk | 1 - mk/include_CLANG.mk | 3 --- mk/include_GCC.mk | 3 --- mk/include_ICC.mk | 3 --- 4 files changed, 10 deletions(-) diff --git a/config.mk b/config.mk index ffbdb6c..ff941e9 100644 --- a/config.mk +++ b/config.mk @@ -4,7 +4,6 @@ TOOLCHAIN ?= ICC MTX_FMT ?= CRS ENABLE_MPI ?= true ENABLE_OPENMP ?= false -HAVE_C23 ?= false FLOAT_TYPE ?= DP # SP for float, DP for double UINT_TYPE ?= U # U for unsigned int, ULL for unsigned long long int diff --git a/mk/include_CLANG.mk b/mk/include_CLANG.mk index afd53fe..fc01999 100644 --- a/mk/include_CLANG.mk +++ b/mk/include_CLANG.mk @@ -14,9 +14,6 @@ endif # Set default C_VERSION = c17 -ifeq ($(strip $(HAVE_C23)),true) -C_VERSION = c23 -endif VERSION = --version CFLAGS = -O3 -ffast-math -std=$(C_VERSION) $(OPENMP) diff --git a/mk/include_GCC.mk b/mk/include_GCC.mk index 775af22..35ee4df 100644 --- a/mk/include_GCC.mk +++ b/mk/include_GCC.mk @@ -13,9 +13,6 @@ endif # Set default C_VERSION = c17 -ifeq ($(strip $(HAVE_C23)),true) -C_VERSION = c23 -endif VERSION = --version CFLAGS = -O3 -ffast-math -std=$(C_VERSION) $(OPENMP) diff --git a/mk/include_ICC.mk b/mk/include_ICC.mk index 31c99e2..5aeabd3 100644 --- a/mk/include_ICC.mk +++ b/mk/include_ICC.mk @@ -13,9 +13,6 @@ endif # Set default C_VERSION = c17 -ifeq ($(strip $(HAVE_C23)),true) -C_VERSION = c23 -endif VERSION = --version CFLAGS = -O3 -ffast-math -xHost -std=$(C_VERSION) $(OPENMP)