From 3aed1353e0b13d52b5e895488860c5015d404451 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Wed, 25 Sep 2024 22:43:59 +0100 Subject: [PATCH 1/3] first pass of quest qft test --- tests/apps/quest/quest.py | 59 ++++++++++ tests/apps/quest/src/cmake_questQFT.sh | 26 +++++ tests/apps/quest/src/qft.cpp | 156 +++++++++++++++++++++++++ 3 files changed, 241 insertions(+) create mode 100644 tests/apps/quest/quest.py create mode 100644 tests/apps/quest/src/cmake_questQFT.sh create mode 100644 tests/apps/quest/src/qft.cpp diff --git a/tests/apps/quest/quest.py b/tests/apps/quest/quest.py new file mode 100644 index 0000000..6ccf1a0 --- /dev/null +++ b/tests/apps/quest/quest.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 + +"""Reframe test for XCompact3D""" + +# Based on original work from: +# Copyright 2016-2022 Swiss National Supercomputing Centre (CSCS/ETH Zurich) +# ReFrame Project Developers. See the top-level LICENSE file for details. +# SPDX-License-Identifier: BSD-3-Clause + +import reframe as rfm +import reframe.utility.sanity as sn + + +@rfm.simple_test +class QuestQFTHugeTest(rfm.RegressionTest): + """Quest Huge 4096 node Test""" + + valid_systems = ["archer2:compute"] + valid_prog_environs = ["PrgEnv-gnu"] + + tags = {"performance", "largescale", "applications"} + + num_nodes = 4 + num_tasks_per_node = 1 + num_cpus_per_task = 128 + num_tasks = num_nodes * num_tasks_per_node + + env_vars = {"OMP_NUM_THREADS": str(num_cpus_per_task), "OMP_PLACES": "cores"} + + time_limit = "10m" + build_system = "Make" + prebuild_cmds = [ + "git clone https://github.com/QuEST-Kit/QuEST.git", + "source cmake_questQFT.sh qft", + ] + builddir = "build" + executable = "build/qft" + executable_opts = ["5"] + modules = ["cmake/3.29.4"] + + + @run_before("compile") + def prepare_build(self): + self.prebuild_cmd = ["git clone https://github.com/QuEST-Kit/QuEST.git .", "source cmake_questQFT.sh qft"] + + @sanity_function + def assert_finished(self): + """Sanity check that simulation finished successfully""" + return sn.assert_found("Total run time:", self.stdout) + + # @performance_function("seconds", perf_key="performance") + # def extract_perf(self): + # """Extract performance value to compare with reference value""" + # return sn.extractsingle( + # r"Averaged time per step \(s\):\s+(?P\S+)", + # self.stdout, + # "time", + # float, + # ) diff --git a/tests/apps/quest/src/cmake_questQFT.sh b/tests/apps/quest/src/cmake_questQFT.sh new file mode 100644 index 0000000..98aefed --- /dev/null +++ b/tests/apps/quest/src/cmake_questQFT.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +TARGET=${1} +BUILD_DIR=build +SOURCE_DIR=QuEST + +mkdir -p ${BUILD_DIR} +cd ${BUILD_DIR} + +USER_SOURCE=../${TARGET}.cpp +OUTPUT_EXE=${TARGET} +DISTRIBUTED=1 +MULTITHREADED=1 +GPUACCELERATED=0 + +CC=cc +CXX=CC + +cmake ../${SOURCE_DIR} \ + -DCMAKE_C_COMPILER=${CC} \ + -DCMAKE_CXX_COMPILER=${CXX} \ + -DUSER_SOURCE=${USER_SOURCE} \ + -DOUTPUT_EXE=${OUTPUT_EXE} \ + -DDISTRIBUTED=${DISTRIBUTED} \ + -DMULTITHREADED=${MULTITHREADED} \ + -DGPUACCELERATED=${GPUACCELERATED} diff --git a/tests/apps/quest/src/qft.cpp b/tests/apps/quest/src/qft.cpp new file mode 100644 index 0000000..86dc7a0 --- /dev/null +++ b/tests/apps/quest/src/qft.cpp @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include "QuEST.h" + +double calcPhaseShif(const int); +void qftQubit(Qureg, const int, const int); +void qft(Qureg, const int); +void writeState(const int * const, const size_t); +void getMonoTime(struct timespec *); +double getElapsedSeconds(const struct timespec * const, + const struct timespec * const); + +int main (int argc, char *argv[]) +{ + int num_qubits; + + if (argc < 2) { + num_qubits = 4; + } else if (argc > 2) { + printf("Error: Too many arguments! Usage: ./qft $NUMBER_OF_QUBITS\n"); + return -1; + } else { + // arcg == 2 + num_qubits = atoi(argv[1]); + if (num_qubits < 1) { + printf("Error: num_qubits < 1, you requested %d qubits\n", num_qubits); + return -1; + } + } + + struct timespec run_start, run_stop; + struct timespec qft_start, qft_stop; + double run_time, qft_time; + + getMonoTime(&run_start); + + // initialise QuEST + QuESTEnv quenv = createQuESTEnv(); + + // create quantum register + Qureg qureg = createQureg(num_qubits, quenv); + + if (!quenv.rank) + printf("Simulating %d-Qubit QFT\n\n", num_qubits); + + // initialise input register to |0..0> + initZeroState(qureg); + + // report model + if (!quenv.rank) { + reportQuregParams(qureg); + printf("\n"); + reportQuESTEnv(quenv); + printf("\n"); + } + + getMonoTime(&qft_start); + + // apply QFT to input register + qft(qureg, num_qubits); + + getMonoTime(&qft_stop); + + if (!quenv.rank) + printf("Total number of gates: %d\n", (num_qubits * (num_qubits+1))/2 ); + + // results + qreal prob_0 = getProbAmp(qureg, 0); + if (!quenv.rank) { + printf("Measured probability amplitude of |0..0> state: %g\n", prob_0); + printf("Calculated probability amplitude of |0..0>, C0 = 1 / 2^%d: %g\n", + num_qubits, 1.0 / pow(2,num_qubits)); + + printf("Measuring final state: (all probabilities should be 0.5)\n"); + } + + int * state = (int *) malloc(num_qubits * sizeof(int)); + qreal * probs = (qreal *) malloc(num_qubits * sizeof(qreal)); + for (int n = 0; n < num_qubits; ++n) { + state[n] = measureWithStats(qureg, n, probs+n); + } + if (!quenv.rank) { + for (int n = 0; n < num_qubits; ++n) + printf("Qubit %d measured in state %d with probability %g\n", + n, state[n], probs[n]); + printf("\n"); + printf("Final state:\n"); + writeState(state, num_qubits); + } + + // free resources and stop timer + free(state); + free(probs); + destroyQureg(qureg, quenv); + + getMonoTime(&run_stop); + + qft_time = getElapsedSeconds(&qft_start, &qft_stop); + run_time = getElapsedSeconds(&run_start, &run_stop); + + if (!quenv.rank) + printf("QFT run time: %gs\nTotal run time: %gs\n", qft_time, run_time); + + // destroy QuESTEnv late because we need MPI rank + destroyQuESTEnv(quenv); + + return 0; +} + +double calcPhaseShift(const int M) { + return ( M_PI / pow(2, (M-1)) ); +} + +void qftQubit(Qureg qureg, const int num_qubits, const int QUBIT_ID) { + int control_id = 0; + double angle = 0.0; + + hadamard(qureg, QUBIT_ID); + int m = 2; + for (int control = QUBIT_ID+1; control < num_qubits; ++control) { + angle = calcPhaseShift(m++); + controlledPhaseShift(qureg, control, QUBIT_ID, angle); + } + + return; +} + +void qft(Qureg qureg, const int num_qubits) { + for (int qid = 0; qid < num_qubits; ++qid) + qftQubit(qureg, num_qubits, qid); + return; +} + +void writeState(const int * const STATE, const size_t num_qubits) { + printf("|"); + for (size_t n = 0; n < num_qubits; ++n) printf("%d", STATE[n]); + printf(">\n"); + return; +} + +void getMonoTime(struct timespec * time) { + clock_gettime(CLOCK_MONOTONIC, time); + return; +} + +double getElapsedSeconds(const struct timespec * const start, +const struct timespec * const stop) { + const unsigned long BILLION = 1000000000UL; + const unsigned long long TOTAL_NS = + BILLION * (stop->tv_sec - start->tv_sec) + + (stop->tv_nsec - start->tv_nsec); + + return (double) TOTAL_NS / BILLION; +} From 46f83ce1ac8778cceab2c086aaf86e0ca312282a Mon Sep 17 00:00:00 2001 From: JPRichings Date: Thu, 26 Sep 2024 11:58:04 +0100 Subject: [PATCH 2/3] quest qft capability job tested in this config 512 nodes --- configuration/archer2.py | 19 +++++++++++++++++++ tests/apps/quest/quest.py | 12 ++++++++---- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/configuration/archer2.py b/configuration/archer2.py index 783c205..c242753 100644 --- a/configuration/archer2.py +++ b/configuration/archer2.py @@ -46,6 +46,25 @@ def command(self, job): "num_sockets": 2, }, }, + { + "name": "compute-capability", + "descr": "Compute nodes during capabilitydays", + "scheduler": "slurm", + "launcher": "srun", + "access": [ + "--hint=nomultithread", + "--distribution=block:block", + "--partition=standard", + "--qos=capabilityday", + ], + "environs": ["PrgEnv-gnu", "PrgEnv-cray", "PrgEnv-aocc"], + "max_jobs": 64, + "processor": { + "num_cpus": 128, + "num_cpus_per_socket": 64, + "num_sockets": 2, + }, + }, { "name": "compute-gpu", "descr": "Compute nodes with AMD GPUs", diff --git a/tests/apps/quest/quest.py b/tests/apps/quest/quest.py index 6ccf1a0..4591243 100644 --- a/tests/apps/quest/quest.py +++ b/tests/apps/quest/quest.py @@ -15,19 +15,19 @@ class QuestQFTHugeTest(rfm.RegressionTest): """Quest Huge 4096 node Test""" - valid_systems = ["archer2:compute"] + valid_systems = ["archer2:compute-capability"] valid_prog_environs = ["PrgEnv-gnu"] tags = {"performance", "largescale", "applications"} - num_nodes = 4 + num_nodes = 512 num_tasks_per_node = 1 num_cpus_per_task = 128 num_tasks = num_nodes * num_tasks_per_node env_vars = {"OMP_NUM_THREADS": str(num_cpus_per_task), "OMP_PLACES": "cores"} - time_limit = "10m" + time_limit = "20m" build_system = "Make" prebuild_cmds = [ "git clone https://github.com/QuEST-Kit/QuEST.git", @@ -35,13 +35,17 @@ class QuestQFTHugeTest(rfm.RegressionTest): ] builddir = "build" executable = "build/qft" - executable_opts = ["5"] + executable_opts = ["41"] modules = ["cmake/3.29.4"] @run_before("compile") def prepare_build(self): self.prebuild_cmd = ["git clone https://github.com/QuEST-Kit/QuEST.git .", "source cmake_questQFT.sh qft"] + + @run_before('run') + def set_num_nodes(self): + self.job.options = [f"--nodes={self.num_nodes}"] @sanity_function def assert_finished(self): From a2181c614083e7d0ad3f18c57c99183a56cef229 Mon Sep 17 00:00:00 2001 From: JPRichings Date: Thu, 26 Sep 2024 12:11:10 +0100 Subject: [PATCH 3/3] Added additional version of Quest test for varrying scales --- tests/apps/quest/Quest.py | 61 +++++++++++++++++++++ tests/apps/quest/{quest.py => QuestHuge.py} | 34 ++++++------ tests/apps/quest/QuestLarge.py | 61 +++++++++++++++++++++ 3 files changed, 138 insertions(+), 18 deletions(-) create mode 100644 tests/apps/quest/Quest.py rename tests/apps/quest/{quest.py => QuestHuge.py} (72%) create mode 100644 tests/apps/quest/QuestLarge.py diff --git a/tests/apps/quest/Quest.py b/tests/apps/quest/Quest.py new file mode 100644 index 0000000..d1501cd --- /dev/null +++ b/tests/apps/quest/Quest.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +# Based on original work from: +# Copyright 2016-2022 Swiss National Supercomputing Centre (CSCS/ETH Zurich) +# ReFrame Project Developers. See the top-level LICENSE file for details. +# SPDX-License-Identifier: BSD-3-Clause + +import reframe as rfm +import reframe.utility.sanity as sn + + +@rfm.simple_test +class QuestQFTTest(rfm.RegressionTest): + """Quest 4 node Test""" + + valid_systems = ["archer2:compute"] + valid_prog_environs = ["PrgEnv-gnu"] + + tags = {"performance", "applications"} + + num_nodes = 4 + num_tasks_per_node = 1 + num_cpus_per_task = 128 + num_tasks = num_nodes * num_tasks_per_node + + env_vars = {"OMP_NUM_THREADS": str(num_cpus_per_task), "OMP_PLACES": "cores"} + + time_limit = "20m" + build_system = "Make" + prebuild_cmds = [ + "git clone https://github.com/QuEST-Kit/QuEST.git", + "source cmake_questQFT.sh qft", + ] + builddir = "build" + executable = "build/qft" + executable_opts = ["34"] + modules = ["cmake/3.29.4"] + + @run_before("compile") + def prepare_build(self): + self.prebuild_cmd = ["git clone https://github.com/QuEST-Kit/QuEST.git .", "source cmake_questQFT.sh qft"] + + @run_before("run") + def set_num_nodes(self): + self.job.options = [f"--nodes={self.num_nodes}"] + + @sanity_function + def assert_finished(self): + """Sanity check that simulation finished successfully""" + return sn.assert_found("Total run time:", self.stdout) + + +# @performance_function("seconds", perf_key="performance") +# def extract_perf(self): +# """Extract performance value to compare with reference value""" +# return sn.extractsingle( +# r"Averaged time per step \(s\):\s+(?P\S+)", +# self.stdout, +# "time", +# float, +# ) diff --git a/tests/apps/quest/quest.py b/tests/apps/quest/QuestHuge.py similarity index 72% rename from tests/apps/quest/quest.py rename to tests/apps/quest/QuestHuge.py index 4591243..bb4697f 100644 --- a/tests/apps/quest/quest.py +++ b/tests/apps/quest/QuestHuge.py @@ -1,7 +1,5 @@ #!/usr/bin/env python3 -"""Reframe test for XCompact3D""" - # Based on original work from: # Copyright 2016-2022 Swiss National Supercomputing Centre (CSCS/ETH Zurich) # ReFrame Project Developers. See the top-level LICENSE file for details. @@ -20,14 +18,14 @@ class QuestQFTHugeTest(rfm.RegressionTest): tags = {"performance", "largescale", "applications"} - num_nodes = 512 + num_nodes = 4096 num_tasks_per_node = 1 num_cpus_per_task = 128 - num_tasks = num_nodes * num_tasks_per_node + num_tasks = num_nodes * num_tasks_per_node env_vars = {"OMP_NUM_THREADS": str(num_cpus_per_task), "OMP_PLACES": "cores"} - time_limit = "20m" + time_limit = "1hr" build_system = "Make" prebuild_cmds = [ "git clone https://github.com/QuEST-Kit/QuEST.git", @@ -35,15 +33,14 @@ class QuestQFTHugeTest(rfm.RegressionTest): ] builddir = "build" executable = "build/qft" - executable_opts = ["41"] + executable_opts = ["44"] modules = ["cmake/3.29.4"] - @run_before("compile") def prepare_build(self): self.prebuild_cmd = ["git clone https://github.com/QuEST-Kit/QuEST.git .", "source cmake_questQFT.sh qft"] - - @run_before('run') + + @run_before("run") def set_num_nodes(self): self.job.options = [f"--nodes={self.num_nodes}"] @@ -52,12 +49,13 @@ def assert_finished(self): """Sanity check that simulation finished successfully""" return sn.assert_found("Total run time:", self.stdout) - # @performance_function("seconds", perf_key="performance") - # def extract_perf(self): - # """Extract performance value to compare with reference value""" - # return sn.extractsingle( - # r"Averaged time per step \(s\):\s+(?P\S+)", - # self.stdout, - # "time", - # float, - # ) + +# @performance_function("seconds", perf_key="performance") +# def extract_perf(self): +# """Extract performance value to compare with reference value""" +# return sn.extractsingle( +# r"Averaged time per step \(s\):\s+(?P\S+)", +# self.stdout, +# "time", +# float, +# ) diff --git a/tests/apps/quest/QuestLarge.py b/tests/apps/quest/QuestLarge.py new file mode 100644 index 0000000..e82fb49 --- /dev/null +++ b/tests/apps/quest/QuestLarge.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +# Based on original work from: +# Copyright 2016-2022 Swiss National Supercomputing Centre (CSCS/ETH Zurich) +# ReFrame Project Developers. See the top-level LICENSE file for details. +# SPDX-License-Identifier: BSD-3-Clause + +import reframe as rfm +import reframe.utility.sanity as sn + + +@rfm.simple_test +class QuestQFTLargeTest(rfm.RegressionTest): + """Quest Large 512 node Test""" + + valid_systems = ["archer2:compute-capability"] + valid_prog_environs = ["PrgEnv-gnu"] + + tags = {"performance", "largescale", "applications"} + + num_nodes = 512 + num_tasks_per_node = 1 + num_cpus_per_task = 128 + num_tasks = num_nodes * num_tasks_per_node + + env_vars = {"OMP_NUM_THREADS": str(num_cpus_per_task), "OMP_PLACES": "cores"} + + time_limit = "20m" + build_system = "Make" + prebuild_cmds = [ + "git clone https://github.com/QuEST-Kit/QuEST.git", + "source cmake_questQFT.sh qft", + ] + builddir = "build" + executable = "build/qft" + executable_opts = ["41"] + modules = ["cmake/3.29.4"] + + @run_before("compile") + def prepare_build(self): + self.prebuild_cmd = ["git clone https://github.com/QuEST-Kit/QuEST.git .", "source cmake_questQFT.sh qft"] + + @run_before("run") + def set_num_nodes(self): + self.job.options = [f"--nodes={self.num_nodes}"] + + @sanity_function + def assert_finished(self): + """Sanity check that simulation finished successfully""" + return sn.assert_found("Total run time:", self.stdout) + + +# @performance_function("seconds", perf_key="performance") +# def extract_perf(self): +# """Extract performance value to compare with reference value""" +# return sn.extractsingle( +# r"Averaged time per step \(s\):\s+(?P\S+)", +# self.stdout, +# "time", +# float, +# )