diff --git a/a1_ln2_openmp/.DS_Store b/a1_ln2_openmp/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/a1_ln2_openmp/.DS_Store differ diff --git a/a1_ln2_openmp.cpp b/a1_ln2_openmp/a1_ln2_openmp.cpp similarity index 100% rename from a1_ln2_openmp.cpp rename to a1_ln2_openmp/a1_ln2_openmp.cpp diff --git a/ln2 b/a1_ln2_openmp/ln2 similarity index 100% rename from ln2 rename to a1_ln2_openmp/ln2 diff --git a/makefile b/a1_ln2_openmp/makefile similarity index 100% rename from makefile rename to a1_ln2_openmp/makefile diff --git a/test.h b/a1_ln2_openmp/test.h similarity index 100% rename from test.h rename to a1_ln2_openmp/test.h diff --git a/a2_ln2_mpi/.DS_Store b/a2_ln2_mpi/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/a2_ln2_mpi/.DS_Store differ diff --git a/a2_ln2_mpi/a2_ln2_mpi.cpp b/a2_ln2_mpi/a2_ln2_mpi.cpp new file mode 100644 index 0000000..29633f2 --- /dev/null +++ b/a2_ln2_mpi/a2_ln2_mpi.cpp @@ -0,0 +1,62 @@ +#include +#include +#include "test.h" + +void runningWithMPITest(); +void computeLn2Test(); + +// **************************************************** +// TODO: parallelize this function with MPI +// **************************************************** + +double ln2(int numTerms) { + int rank, numProcs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &numProcs); + + int start = 1 + (numTerms / numProcs) * rank; + int end = (numTerms / numProcs) * (rank + 1); + + double localSum = 0; + + for (int i = start; i < end + 1; ++i) { + int sign = 1; + if (i % 2 == 0) { + sign = -1; + } + localSum += sign / static_cast(i); + } + + double globalSum; + MPI_Allreduce(&localSum, &globalSum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + return globalSum; +} + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + runningWithMPITest(); + computeLn2Test(); + MPI_Finalize(); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +// Verify that the program actually runs with MPI +void runningWithMPITest() { + int numProc; + MPI_Comm_size(MPI_COMM_WORLD, &numProc); + check(numProc, 4); +} + +// Verify correct functionality of ln2() +void computeLn2Test() { + check(ln2(4), 0.5833333333333333); + check(ln2(8), 0.6345238095238095); + check(ln2(12), 0.6532106782106781); + check(ln2(100), 0.688172179310195); + check(ln2(1e6), 0.6931466805602525); +} diff --git a/a2_ln2_mpi/ln2 b/a2_ln2_mpi/ln2 new file mode 100644 index 0000000..e82b111 Binary files /dev/null and b/a2_ln2_mpi/ln2 differ diff --git a/a2_ln2_mpi/makefile b/a2_ln2_mpi/makefile new file mode 100644 index 0000000..0b82dc5 --- /dev/null +++ b/a2_ln2_mpi/makefile @@ -0,0 +1,5 @@ +all: a2_ln2_mpi.cpp + mpicxx a2_ln2_mpi.cpp -std=c++17 -o ln2 + +run: all + mpirun -np 4 ./ln2 \ No newline at end of file diff --git a/a2_ln2_mpi/test.h b/a2_ln2_mpi/test.h new file mode 100644 index 0000000..232be71 --- /dev/null +++ b/a2_ln2_mpi/test.h @@ -0,0 +1,290 @@ +/** test.h, an extremly simple test framework. + * Version 1.4 + * Copyright (C) 2022-2023 Tobias Kreilos, Offenburg University of Applied + * Sciences + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a3_ln2_hybrid/a3_ln2_hybrid.cpp b/a3_ln2_hybrid/a3_ln2_hybrid.cpp new file mode 100644 index 0000000..d6fc076 --- /dev/null +++ b/a3_ln2_hybrid/a3_ln2_hybrid.cpp @@ -0,0 +1,69 @@ +#include +#include +#include +#include "test.h" + +void runningHybridTest(); +void computeLn2Test(); + +// **************************************************** +// TODO: parallelize this function with MPI and OpenMP +// **************************************************** + +double ln2(int numTerms) { + int rank, numProcs; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &numProcs); + + int start = 1 + (numTerms / numProcs) * rank; + int end = (numTerms / numProcs) * (rank + 1); + + double localSum = 0; + + #pragma omp parallel for reduction(+ : localSum) + for (int i = start; i < end + 1; ++i) { + int sign = 1; + if (i % 2 == 0) { + sign = -1; + } + localSum += sign / static_cast(i); + } + + double globalSum; + MPI_Allreduce(&localSum, &globalSum, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + return globalSum; +} + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + runningHybridTest(); + computeLn2Test(); + MPI_Finalize(); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +// Verify that the program actually runs with MPI and OpenMP +void runningHybridTest() { + int numProc; + MPI_Comm_size(MPI_COMM_WORLD, &numProc); +#pragma omp parallel + { + int numThreads = omp_get_num_threads(); + check(numProc, 2); + check(numThreads, 2); + } +} + +// Verify correct functionality of ln2() +void computeLn2Test() { + check(ln2(4), 0.5833333333333333); + check(ln2(8), 0.6345238095238095); + check(ln2(12), 0.6532106782106781); + check(ln2(100), 0.688172179310195); + check(ln2(1e6), 0.6931466805602525); +} diff --git a/a3_ln2_hybrid/ln2 b/a3_ln2_hybrid/ln2 new file mode 100644 index 0000000..0206498 Binary files /dev/null and b/a3_ln2_hybrid/ln2 differ diff --git a/a3_ln2_hybrid/makefile b/a3_ln2_hybrid/makefile new file mode 100644 index 0000000..8bf29ad --- /dev/null +++ b/a3_ln2_hybrid/makefile @@ -0,0 +1,5 @@ +all: a3_ln2_hybrid.cpp + mpicxx a3_ln2_hybrid.cpp -fopenmp -o ln2 + +run: all + mpirun -np 2 -x OMP_NUM_THREADS=2 ./ln2 \ No newline at end of file diff --git a/a3_ln2_hybrid/test.h b/a3_ln2_hybrid/test.h new file mode 100644 index 0000000..232be71 --- /dev/null +++ b/a3_ln2_hybrid/test.h @@ -0,0 +1,290 @@ +/** test.h, an extremly simple test framework. + * Version 1.4 + * Copyright (C) 2022-2023 Tobias Kreilos, Offenburg University of Applied + * Sciences + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a4_minimum_search/.DS_Store b/a4_minimum_search/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/a4_minimum_search/.DS_Store differ diff --git a/a4_minimum_search/a4_min.cpp b/a4_minimum_search/a4_min.cpp new file mode 100644 index 0000000..2a1b332 --- /dev/null +++ b/a4_minimum_search/a4_min.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include "test.h" + +void findMinimumTest(); + +// **************************************************** +// TODO: find and fix the data race in this function +// **************************************************** +double findMinimum(std::function f, + double xMin, + double xMax, + int n) { + + double globalMin = f(xMax); + double localMin = f(xMax); + +#pragma omp parallel firstprivate(localMin) shared(globalMin) + { + double range = xMax - xMin; + double step = range / n; + +#pragma omp for schedule(static) + for (int i = 0; i < n; ++i) { + double x = xMin + step * i; + double fx = f(x); + if (fx < localMin) { + localMin = fx; + } + } + + #pragma omp critical + if (localMin < globalMin) { + globalMin = localMin; + } + } + + return globalMin; +} + +int main() { + findMinimumTest(); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +double f0(double x) { + return (x - 2) * (x - 2); +} + +double f1(double x) { + return sin(x) + 1. / 5. * x * x * x * x - 3. * (x - 3) * (x - 3); +}; + +double f2(double x) { + return sin(x) + x * x; +} + +double f3(double x) { + return x*x*x*x - x*x; +} + +void findMinimumTest() { + check(findMinimum(f0, -5, 5, 1e6), 0); + check(findMinimum(f1, -5, 5, 1e6), -96.68321722021884); + check(findMinimum(f2, -5, 5, 1e6), -0.2324655751423368); + check(findMinimum(f3, -2, 2, 1e7), -0.25); +} \ No newline at end of file diff --git a/a4_minimum_search/makefile b/a4_minimum_search/makefile new file mode 100644 index 0000000..5bd1b96 --- /dev/null +++ b/a4_minimum_search/makefile @@ -0,0 +1,5 @@ +build: a4_min.cpp + g++ -std=c++17 a4_min.cpp -fopenmp -o min + +run: build + ./min \ No newline at end of file diff --git a/a4_minimum_search/min b/a4_minimum_search/min new file mode 100644 index 0000000..eaa3b31 Binary files /dev/null and b/a4_minimum_search/min differ diff --git a/a4_minimum_search/test.h b/a4_minimum_search/test.h new file mode 100644 index 0000000..232be71 --- /dev/null +++ b/a4_minimum_search/test.h @@ -0,0 +1,290 @@ +/** test.h, an extremly simple test framework. + * Version 1.4 + * Copyright (C) 2022-2023 Tobias Kreilos, Offenburg University of Applied + * Sciences + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a5_mpi_deadlock/.DS_Store b/a5_mpi_deadlock/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/a5_mpi_deadlock/.DS_Store differ diff --git a/a5_mpi_deadlock/a5_deadlock.cpp b/a5_mpi_deadlock/a5_deadlock.cpp new file mode 100644 index 0000000..b493380 --- /dev/null +++ b/a5_mpi_deadlock/a5_deadlock.cpp @@ -0,0 +1,49 @@ +#include +#include +#include "test.h" + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + + int rank, numProc; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &numProc); + + int sendBuf; + int recvBuf; + int val = 0; + MPI_Status status; + + // send and receive message on rank 0 + if (rank == 0) { + sendBuf = 123; + MPI_Recv(&recvBuf, 1, MPI_INT, 1, 0, MPI_COMM_WORLD, &status); + std::cout << "Rank 0 received " << recvBuf << "\n"; + + MPI_Send(&sendBuf, 1, MPI_INT, 1, 0, MPI_COMM_WORLD); + std::cout << "Rank 0 sent " << sendBuf << " to rank 1\n"; + + check(recvBuf, 456); + } + + // send and receive message on rank 1 + if (rank == 1) { + sendBuf = 456; + MPI_Send(&sendBuf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); + std::cout << "Rank 1 sent " << sendBuf << " to rank 0\n"; + + MPI_Recv(&recvBuf, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, &status); + std::cout << "Rank 1 received " << recvBuf << "\n"; + + check(recvBuf, 123); + } + + // Broadcast value from rank 0 to all others + if (rank == 0) val = 789; + MPI_Bcast(&val, 1, MPI_DOUBLE, 0, MPI_COMM_WORLD); + + // Verify that broadcast message arrived correctly everywhere + check(val, 789); + + MPI_Finalize(); +} \ No newline at end of file diff --git a/a5_mpi_deadlock/deadlock b/a5_mpi_deadlock/deadlock new file mode 100644 index 0000000..dd7608d Binary files /dev/null and b/a5_mpi_deadlock/deadlock differ diff --git a/a5_mpi_deadlock/makefile b/a5_mpi_deadlock/makefile new file mode 100644 index 0000000..e56bc19 --- /dev/null +++ b/a5_mpi_deadlock/makefile @@ -0,0 +1,5 @@ +all: a5_deadlock.cpp + mpicxx a5_deadlock.cpp -std=c++17 -o deadlock + +run: all + mpirun -np 3 ./deadlock \ No newline at end of file diff --git a/a5_mpi_deadlock/test.h b/a5_mpi_deadlock/test.h new file mode 100644 index 0000000..232be71 --- /dev/null +++ b/a5_mpi_deadlock/test.h @@ -0,0 +1,290 @@ +/** test.h, an extremly simple test framework. + * Version 1.4 + * Copyright (C) 2022-2023 Tobias Kreilos, Offenburg University of Applied + * Sciences + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file diff --git a/a6_send_column/.DS_Store b/a6_send_column/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/a6_send_column/.DS_Store differ diff --git a/a6_send_column/a6_send_column.cpp b/a6_send_column/a6_send_column.cpp new file mode 100644 index 0000000..4b08d3f --- /dev/null +++ b/a6_send_column/a6_send_column.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include "test.h" +#include "matrix.h" + +using namespace HPC; + +// For simplicity, store rank in a global variable +int rank; + +// **************************************************** +// TODO: modify this function to use a custom data +// type instead of sending each entry separately. +// **************************************************** + +// Send one column of the matrix from rank 0 to rank 1 +void sendColumn(Matrix& m, int col) { + + // Store MPI_Reqeuests + std::vector req(m.dim1()); + + // Send/receive each entry of the column separately + for (int i=0; i stat(m.dim1()); + MPI_Waitall(req.size(), req.data(), stat.data()); +} + +// Do not change anything below this line! +// You may use the tests below to verify that your +// program works correctly. +// **************************************************** + +// Create a matrix with some contents +Matrix createMatrix(int n) { + Matrix m(n); + for (int i = 0; i < n; ++i) { + for (int j = 0; j < n; ++j) { + m(i, j) = 2 * i + i * j + 1; + } + } + return m; +} + +void sendColumnTest() { + int n = 3; + + Matrix m(n); + if (rank == 0) { + m = createMatrix(n); + } + + sendColumn(m, 1); + + if (rank == 0) { + check(m(0, 0), 1); + check(m(0, 1), 1); + check(m(0, 2), 1); + check(m(1, 0), 3); + check(m(1, 1), 4); + check(m(1, 2), 5); + check(m(2, 0), 5); + check(m(2, 1), 7); + check(m(2, 2), 9); + } + + if (rank == 1) { + check(m(0, 0), 0); + check(m(0, 1), 1); + check(m(0, 2), 0); + check(m(1, 0), 0); + check(m(1, 1), 4); + check(m(1, 2), 0); + check(m(2, 0), 0); + check(m(2, 1), 7); + check(m(2, 2), 0); + } +} + + +int main(int argc, char* argv[]) { + MPI_Init(&argc, &argv); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + sendColumnTest(); + + MPI_Finalize(); +} \ No newline at end of file diff --git a/a6_send_column/makefile b/a6_send_column/makefile new file mode 100644 index 0000000..8e69866 --- /dev/null +++ b/a6_send_column/makefile @@ -0,0 +1,5 @@ +build: a6_send_column.cpp test.h matrix.h + mpicxx -std=c++17 a6_send_column.cpp -o send-column + +run: build + mpirun -np 2 -x OMP_NUM_THREADS=2 ./send-column \ No newline at end of file diff --git a/a6_send_column/matrix.h b/a6_send_column/matrix.h new file mode 100644 index 0000000..b8caacc --- /dev/null +++ b/a6_send_column/matrix.h @@ -0,0 +1,324 @@ +/** + * matrix.h a very simplistic class for m times n matrices. + */ + +#ifndef MATRIX_H +#define MATRIX_H + +#include +#include +#include +#include + +namespace HPC { + +// A very simplistic vector class for vectors of size n +class Vector { + public: + // constructors + Vector(int n) : n_(n), data_(n_, 0) {} + Vector(const Vector& other) = default; + Vector(Vector&& other) = default; + ~Vector() = default; + + // assignment operators + Vector& operator=(const Vector& other) = default; + Vector& operator=(Vector&& other) = default; + + // element access + double& operator()(int i) { return data_[i]; } + const double& operator()(int i) const { return data_[i]; } + + // getter functions for the dimensions + int dim() const { return n_; } + + // comparison operators + bool operator==(const Vector& b) { return (data_ == b.data_); } + bool operator!=(const Vector& b) { return (data_ != b.data_); } + + // addition, substraction, multiplication with scalars + Vector& operator+=(const Vector& b) { + for (int i = 0; i < n_; ++i) { + operator()(i) += b(i); + } + return *this; + } + + Vector& operator-=(const Vector& b) { + for (int i = 0; i < n_; ++i) { + operator()(i) -= b(i); + } + return *this; + } + + Vector& operator*=(double x) { + for (int i = 0; i < n_; ++i) { + operator()(i) *= x; + } + return *this; + } + + Vector& operator/=(double x) { + for (int i = 0; i < n_; ++i) { + operator()(i) /= x; + } + + return *this; + } + + double dot(const Vector& other) const { + double sum = 0; + for (int i = 0; i < n_; ++i) { + sum += operator()(i) * other(i); + } + return sum; + } + + private: + int n_; // vector dimension + std::vector data_; // the vectors entries +}; + +inline double dot(const Vector& v1, const Vector& v2) { + return v1.dot(v2); +} + +// Print the vector as a table +inline std::ostream& operator<<(std::ostream& os, const Vector& a) { + const int width = 10; + const int precision = 4; + + const auto originalPrecision = os.precision(); + os << std::setprecision(precision); + + for (int i = 0; i < a.dim(); ++i) { + os << std::setw(width) << a(i) << " "; + } + + os << "\n"; + + os << std::setprecision(originalPrecision); + return os; +} + +// A very simple class for m times n matrices +class Matrix { + public: + // constructors + Matrix() : Matrix (0, 0) {} + Matrix(int m, int n) : m_(m), n_(n), data_(m_ * n_, 0) {} + Matrix(std::pair dim) : Matrix(dim.first, dim.second) {} + Matrix(int n) : Matrix(n, n) {} + Matrix(const Matrix& other) = default; + Matrix(Matrix&& other) = default; + ~Matrix() = default; + + // assignment operators + Matrix& operator=(const Matrix& other) = default; + Matrix& operator=(Matrix&& other) = default; + + // element access + double& operator()(int i, int j) { return data_[i * n_ + j]; } + const double& operator()(int i, int j) const { return data_[i * n_ + j]; } + + // getter functions for the dimensions + std::pair dim() const { return std::pair(m_, n_); } + int dim1() const { return m_; } + int dim2() const { return n_; } + int numEntries() const { return data_.size(); } + + // comparison operators + bool operator==(const Matrix& b) { return (data_ == b.data_); } + bool operator!=(const Matrix& b) { return (data_ != b.data_); } + + // addition, substraction, multiplication with scalars + Matrix& operator+=(const Matrix& b) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) += b(i, j); + } + } + return *this; + } + + Matrix& operator-=(const Matrix& b) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) -= b(i, j); + } + } + return *this; + } + + Matrix& operator*=(double x) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) *= x; + } + } + return *this; + } + + Matrix& operator/=(double x) { + for (int i = 0; i < m_; ++i) { + for (int j = 0; j < n_; ++j) { + operator()(i, j) /= x; + } + } + return *this; + } + + public: + int m_; // first dimension + int n_; // second dimension + std::vector data_; // the matrix' entries +}; + +// Print the matrix as a table +inline std::ostream& operator<<(std::ostream& os, const Matrix& a) { + const int width = 10; + const int precision = 4; + + const auto originalPrecision = os.precision(); + os << std::setprecision(precision); + + for (int i = 0; i < a.dim1(); ++i) { + for (int j = 0; j < a.dim2(); ++j) { + os << std::setw(width) << a(i, j) << " "; + } + if (i != a.dim1() - 1) + os << "\n"; + } + + os << std::setprecision(originalPrecision); + return os; +} + +inline Matrix operator*(const Matrix& a, const Matrix& b) { + if (a.dim2() == b.dim1()) { + int m = a.dim1(); + int n = a.dim2(); + int p = b.dim2(); + Matrix c(m, p); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < p; ++j) { + for (int k = 0; k < n; ++k) { + c(i, j) += a(i, k) * b(k, j); + } + } + } + return c; + } else { + return Matrix(0, 0); + } +} + +inline bool equalWithinRange(const Matrix& a, const Matrix& b, double eps = 1e-12) { + if (a.dim2() != b.dim1()) + return false; + + int m = a.dim1(); + int n = a.dim2(); + for (int i = 0; i < m; ++i) { + for (int j = 0; j < n; ++j) { + if (fabs(a(i, j) - b(i, j)) > eps) { + return false; + } + } + } + + return true; +} + +// A very simple class for "3D-Matrices" (tensors) with dimension l x m x n +class Matrix3D { + public: + // constructors + Matrix3D(int l, int m, int n) : l_(l), m_(m), n_(n), data_(l) { + for (int i = 0; i < l_; ++i) { + data_[i] = std::vector>(m_); + for (int j = 0; j < m_; ++j) { + data_[i][j] = std::vector(n_, 0); + } + } + } + Matrix3D(int n) : Matrix3D(n, n, n) {} + Matrix3D(const Matrix3D& other) = default; + Matrix3D(Matrix3D&& other) = default; + ~Matrix3D() = default; + + // assignment operators + Matrix3D& operator=(const Matrix3D& other) = default; + Matrix3D& operator=(Matrix3D&& other) = default; + + // element access + double& operator()(int i, int j, int k) { return data_[i][j][k]; } + const double& operator()(int i, int j, int k) const { return data_[i][j][k]; } + + // getter functions for the dimensions + int dim1() const { return l_; } + int dim2() const { return m_; } + int dim3() const { return n_; } + + // comparison operators + bool operator==(const Matrix3D& b) { return (data_ == b.data_); } + bool operator!=(const Matrix3D& b) { return (data_ != b.data_); } + + // addition + Matrix3D& operator+=(const Matrix3D& b) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) += b(i, j, k); + } + } + } + return *this; + } + + // substraction + Matrix3D& operator-=(const Matrix3D& b) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) -= b(i, j, k); + } + } + } + return *this; + } + + // scalar multiplication + Matrix3D& operator*=(double x) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) *= x; + } + } + } + return *this; + } + + // scalar division + Matrix3D& operator/=(double x) { + for (int i = 0; i < l_; ++i) { + for (int j = 0; j < m_; ++j) { + for (int k = 0; k < n_; ++k) { + operator()(i, j, k) /= x; + } + } + } + return *this; + } + + private: + int l_; // first dimension + int m_; // second dimension + int n_; // third dimension + std::vector>> data_; // the tensors' entries +}; + +} // namespace HPC + +#endif // MATRIX_H \ No newline at end of file diff --git a/a6_send_column/send-column b/a6_send_column/send-column new file mode 100644 index 0000000..fac0594 Binary files /dev/null and b/a6_send_column/send-column differ diff --git a/a6_send_column/test.h b/a6_send_column/test.h new file mode 100644 index 0000000..232be71 --- /dev/null +++ b/a6_send_column/test.h @@ -0,0 +1,290 @@ +/** test.h, an extremly simple test framework. + * Version 1.4 + * Copyright (C) 2022-2023 Tobias Kreilos, Offenburg University of Applied + * Sciences + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * + * + * The framework defines a function check(a,b) that can be called with + * parameters of different types. The function asserts + * that the two paramters are equal (within a certain, predefined range for + * floating point numbers) and prints the result of the comparison on the + * command line. Additionally a summary of all tests is printed at the end of + * the program. + * Additionally there is TEST macro, which you can place outside main to group + * tests together. Code in the macro is automatically executed at the beginning + * of the program. + * The file also defines a class InstanceCount, that can be used to + * count how many instances of an object are still alive at the end of a + * program. To use it, derive your class from InstanceCount and the + * message is automatically printed at the end of the program. + */ + +#ifndef VERY_SIMPLE_TEST_H + #define VERY_SIMPLE_TEST_H + + #include + #include + #include + #include + + /** Simple macro to execute the code that follows the macro (without call from + * main) + * + * Define a class, that is directly instantiated + * and contains the test code in the constructor. + * + * Usage: + * TEST(MyTest) + * { + * // test code + * } + */ + #define TEST(name) \ + struct _TestClass##name { \ + _TestClass##name(); \ + } _TestClass##name##Instance; \ + _TestClass##name::_TestClass##name() + +// Use a namespace to hide implementation details +namespace Test::Detail { +/** + * Make it possible to print the underlying value of class enums with ostream + * + * The expression typename std::enable_if::value, + * std::ostream>::type decays to ostream if the type T is an enum. Otherwise, + * the function is not generated. + */ +template +std::ostream& operator<<( + typename std::enable_if::value, std::ostream>::type& stream, + const T& e) { + return stream << static_cast::type>(e); +} + +/** + * Convert anything to a string. + */ +template +std::string toString(const T& t) { + std::ostringstream ss; + ss << t; + return "\"" + ss.str() + "\""; +} + +/** + * Convert bools to string "true" or "false" instead of 0 and 1 + */ +template <> +inline std::string toString(const bool& b) { + return b ? "\"true\"" : "\"false\""; +} + +/** + * Comparison function for different types + */ +template +bool isEqual(const T& t1, const T& t2) { + return t1 == t2; +} + +/** + * Double values are equal if they differ no more than 1e-12 + */ +template <> +inline bool isEqual(const double& expectedValue, + const double& actualValue) { + const double epsilon = 1e-12; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * Float values are equal if they differ no more than 1e-6 + */ +template <> +inline bool isEqual(const float& expectedValue, + const float& actualValue) { + const double epsilon = 1e-6; + const double distance = fabs(actualValue - expectedValue); + return (distance < epsilon); +} + +/** + * This class realizes some basics of the test framework. + * Test summary is printed in the destructor. + * Apart from that, the class implements counting of total and failed tests, + * comparison of floating point numbers within sensible boundaries and prints + * the result of each test on the command line. + */ +class Test { + public: + /** + * Test class is a Singleton + */ + static Test& instance() { + static Test test; + return test; + } + + /** + * the main entry point for tests. Test two values for equality and output the + * result. + */ + template + bool check(const T& expectedValue, const T& actualValue) { + bool testResult = isEqual(expectedValue, actualValue); + if (testResult == true) { + registerPassingTest(); + #pragma omp critical + std::cout << "Test successful! Expected value == actual value (=" + << toString(expectedValue) << ")" << std::endl; + } else { + registerFailingTest(); + #pragma omp critical + std::cout << "Error in test: expected value " << toString(expectedValue) + << ", but actual value was " << toString(actualValue) + << std::endl; + } + + return testResult; + } + + private: + /** + * On destruction, print a summary of all tests. + */ + ~Test() { + std::cout << "\n--------------------------------------" << std::endl; + std::cout << "Test summary:" << std::endl; + std::cout << "Executed tests: " << numTests_ << std::endl; + std::cout << "Failed tests: " << numFailedTests_ << std::endl; + } + + void registerPassingTest() { numTests_++; } + + void registerFailingTest() { + numTests_++; + numFailedTests_++; + } + + /** + * For statistics + */ + std::atomic numTests_ = 0; + + /** + * For statistics + */ + std::atomic numFailedTests_ = 0; +}; + +template +class InstanceCounterHelper { + public: + ~InstanceCounterHelper() { + std::cout << "The remaining number of objects of type " << typeid(T).name() + << " at the end of the program is " << count; + if (count > 0) + std::cout << " (NOT zero!)"; + std::cout << "\nThe total number of objects created was " << total + << std::endl; + } + + void increment() { + count++; + total++; + } + + void decrement() { count--; } + + private: + std::atomic count = 0; + std::atomic total = 0; +}; + +} // namespace Test::Detail + +/** + * Count the instances of a class T. + * Result gets printed automatically at the end of the program. + * To use it, inherit T from InstanceCounter, e.g. + * class MyClass : InstanceCounter + */ +template +class InstanceCounter { + public: + InstanceCounter() { counter().increment(); } + + InstanceCounter(const InstanceCounter&) { counter().increment(); } + + InstanceCounter(const InstanceCounter&&) { counter().increment(); } + + virtual ~InstanceCounter() { counter().decrement(); } + + Test::Detail::InstanceCounterHelper& counter() { + static Test::Detail::InstanceCounterHelper c; + return c; + } +}; + +/** + * Check if the expected value is equal to the actual value. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +template +void check(const T1& actualValue, const T2& expectedValue) { + const T1& expectedValueCasted{ + expectedValue}; // allows conversion in general, but avoids narrowing + // conversion + Test::Detail::Test::instance().check(expectedValueCasted, actualValue); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const int& actualValue, const double& expectedValue) { + Test::Detail::Test::instance().check(expectedValue, + static_cast(actualValue)); +} + +// allow conversion from int to double explicitely +template <> +inline void check(const double& actualValue, const int& expectedValue) { + Test::Detail::Test::instance().check(static_cast(expectedValue), + actualValue); +} + +/** + * Check if the entered value is true. + * Result is printed on the command line and at the end of the program, a + * summary of all tests is printed. + */ +inline void check(bool a) { + Test::Detail::Test::instance().check(true, a); +} + +#endif // VERY_SIMPLE_TEST_H + +/** + * V1.0: Creation of franework + * V1.1: make check(bool) inline, automatically convert expected value type to + * actual value type + * V1.2: added possibilty to count constructions and destructions of some type + * V1.3: tweaks on check for int and double types + * V1.4: Adding thread safety in OpenMP programs (not general thread safety, as + * OpenMP and std::thread might not play along) + */ \ No newline at end of file