├── .gitignore ├── LICENSE ├── Labs ├── 2022-23 │ ├── lab01 │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── hint │ │ │ │ └── main.cpp │ │ │ └── solution │ │ │ │ └── main.cpp │ │ ├── ex02 │ │ │ ├── hint │ │ │ │ ├── horner.hpp │ │ │ │ └── main.cpp │ │ │ ├── solution-advanced │ │ │ │ ├── horner.hpp │ │ │ │ ├── main.cpp │ │ │ │ └── params.dat │ │ │ └── solution │ │ │ │ ├── horner.hpp │ │ │ │ └── main.cpp │ │ └── ex03 │ │ │ ├── smaller.cpp │ │ │ ├── smaller.s │ │ │ ├── toupper.cpp │ │ │ └── toupper.s │ ├── lab02 │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── hint │ │ │ │ └── main.cpp │ │ │ └── solution │ │ │ │ └── main.cpp │ │ ├── ex02 │ │ │ ├── hint │ │ │ │ └── main.cpp │ │ │ └── solution │ │ │ │ └── main.cpp │ │ └── ex03 │ │ │ ├── hint │ │ │ ├── main.cpp │ │ │ └── newton.hpp │ │ │ └── solution │ │ │ ├── main.cpp │ │ │ └── newton.hpp │ ├── lab03 │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── main.cpp │ │ │ └── solution.cpp │ │ └── ex02 │ │ │ ├── solution-advanced │ │ │ └── main.cpp │ │ │ ├── step-1 │ │ │ ├── hint.cpp │ │ │ └── solution.cpp │ │ │ ├── step-2 │ │ │ ├── hint.cpp │ │ │ └── solution.cpp │ │ │ └── step-3 │ │ │ ├── hint.cpp │ │ │ └── solution.cpp │ ├── lab04 │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── helloworld.cpp │ │ │ ├── hint.cpp │ │ │ └── solution.cpp │ │ ├── ex02 │ │ │ ├── hint.cpp │ │ │ ├── serial.cpp │ │ │ └── solution.cpp │ │ └── ex03 │ │ │ ├── hint.cpp │ │ │ ├── serial.cpp │ │ │ └── solution.cpp │ ├── lab05 │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── hint.cpp │ │ │ └── solution.cpp │ │ ├── ex02 │ │ │ ├── hint.cpp │ │ │ ├── serial.cpp │ │ │ └── solution.cpp │ │ ├── ex03 │ │ │ ├── hint.cpp │ │ │ ├── serial.cpp │ │ │ └── solution.cpp │ │ └── utils.hpp │ └── lab06 │ │ ├── README.md │ │ ├── ex01 │ │ ├── hint.cpp │ │ ├── solution_v1.cpp │ │ └── solution_v2.cpp │ │ ├── ex02 │ │ ├── serial.cpp │ │ └── solution.cpp │ │ ├── ex03 │ │ ├── hint.cpp │ │ └── solution.cpp │ │ └── ex04 │ │ ├── serial.cpp │ │ └── solution.cpp ├── 2023-24 │ ├── README.md │ ├── bash-intro │ │ ├── an-intro-to-bash.md │ │ ├── an-intro-to-bash.pdf │ │ └── assets │ │ │ ├── moving_cli.png │ │ │ ├── shell.png │ │ │ └── terminal.png │ ├── docker-bugfix-mpi │ │ ├── README.md │ │ └── assets │ │ │ └── docker-mpi-error.png │ ├── lab00-setup │ │ ├── README.md │ │ └── assets │ │ │ ├── Docker-F1-pt2.png │ │ │ ├── Docker-F1.png │ │ │ ├── Docker-ext.png │ │ │ ├── WSL-F1.png │ │ │ ├── WSL-ext.png │ │ │ ├── config-compiler.png │ │ │ ├── config-include-std.png │ │ │ ├── eigen-error.png │ │ │ ├── extensions.png │ │ │ ├── installation-flowchart.png │ │ │ ├── lightbulb-edit-include-path.png │ │ │ ├── path-click-pt2.png │ │ │ └── path-click.png │ ├── lab01-compiling │ │ ├── compiling.pdf │ │ └── doc │ │ │ ├── compile_steps.png │ │ │ ├── compiling.tex │ │ │ └── template.png │ ├── lab02-stack-and-classes │ │ ├── README.md │ │ ├── assets │ │ │ ├── Newton_iteration.svg │ │ │ ├── stack.png │ │ │ └── stackmemory4.jpg │ │ ├── ex01 │ │ │ ├── hint │ │ │ │ └── main.cpp │ │ │ └── solution │ │ │ │ ├── main_v1.cpp │ │ │ │ └── main_v2.cpp │ │ └── ex02 │ │ │ ├── hint │ │ │ ├── main.cpp │ │ │ └── newton.hpp │ │ │ └── solution │ │ │ ├── main.cpp │ │ │ ├── newton.cpp │ │ │ └── newton.hpp │ ├── lab03-stl-and-templates │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── step-1 │ │ │ │ ├── hint.cpp │ │ │ │ └── solution.cpp │ │ │ ├── step-2 │ │ │ │ ├── hint.cpp │ │ │ │ └── solution.cpp │ │ │ ├── step-3 │ │ │ │ └── solution.cpp │ │ │ └── step-4 │ │ │ │ └── solution.cpp │ │ └── homework │ │ │ ├── check.hpp │ │ │ ├── main.cpp │ │ │ └── solution.cpp │ ├── lab04-optimization-profiling │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── start.cpp │ │ │ ├── step1.cpp │ │ │ ├── step2.cpp │ │ │ ├── step3.cpp │ │ │ └── step4.cpp │ │ ├── extra-padding │ │ │ └── main.cpp │ │ └── homework │ │ │ ├── integer-list.cpp │ │ │ └── integer-list.hpp │ ├── lab05-mpi │ │ ├── README.md │ │ ├── ex01 │ │ │ ├── helloworld.cpp │ │ │ ├── hint.cpp │ │ │ └── solution.cpp │ │ ├── ex02 │ │ │ ├── hint.cpp │ │ │ ├── serial.cpp │ │ │ └── solution.cpp │ │ └── ex03 │ │ │ ├── hint.cpp │ │ │ └── solution.cpp │ └── lab06-openmp │ │ ├── README.md │ │ ├── ex01 │ │ └── solution.cpp │ │ ├── ex02 │ │ ├── hint.cpp │ │ ├── solution_v1.cpp │ │ └── solution_v2.cpp │ │ ├── ex03 │ │ ├── serial.cpp │ │ └── solution.cpp │ │ └── ex04 │ │ ├── hint.cpp │ │ └── solution.cpp └── 2024-25 │ ├── README.md │ ├── exercise_ball │ ├── include │ │ └── utils.h │ ├── problem_statement.pdf │ └── src │ │ ├── Makefile │ │ ├── main.cpp │ │ └── utils.cpp │ ├── lab00-setup │ ├── README.md │ └── assets │ │ ├── Docker-F1-pt2.png │ │ ├── Docker-F1.png │ │ ├── Docker-ext.png │ │ ├── WSL-F1.png │ │ ├── WSL-ext.png │ │ ├── config-compiler.png │ │ ├── config-include-std.png │ │ ├── eigen-error.png │ │ ├── extensions.png │ │ ├── installation-flowchart.png │ │ ├── lightbulb-edit-include-path.png │ │ ├── path-click-pt2.png │ │ └── path-click.png │ ├── lab01-compiling │ └── examples │ │ ├── REAME.txt │ │ ├── include │ │ └── exe.h │ │ └── src │ │ ├── Makefile │ │ ├── exe.cpp │ │ └── main.cpp │ ├── lab02-classes │ ├── README.md │ ├── assets │ │ ├── Newton_iteration.svg │ │ ├── stack.png │ │ └── stackmemory4.jpg │ ├── exe_1 │ │ ├── .DS_Store │ │ ├── include │ │ │ ├── .DS_Store │ │ │ └── utils.h │ │ └── src │ │ │ ├── .DS_Store │ │ │ ├── Makefile │ │ │ ├── main.cpp │ │ │ └── utils.cpp │ ├── exe_2 │ │ ├── include │ │ │ └── utils.h │ │ └── src │ │ │ ├── Makefile │ │ │ ├── main.cpp │ │ │ └── utils.cpp │ └── memory │ │ ├── Makefile │ │ └── memory.cpp │ ├── lab03-stl-and-templates │ ├── README.md │ └── exe │ │ └── src │ │ ├── Makefile │ │ ├── hint.cpp │ │ └── solution.cpp │ ├── lab04-optimization-profiling │ ├── README.md │ ├── ex01 │ │ ├── start.cpp │ │ ├── step1.cpp │ │ ├── step2.cpp │ │ ├── step3.cpp │ │ └── step4.cpp │ └── homework │ │ ├── integer-list.cpp │ │ └── integer-list.hpp │ ├── lab05-mpi │ ├── README.md │ ├── ex01 │ │ ├── helloworld.cpp │ │ ├── hint.cpp │ │ └── sol.cpp │ ├── ex02 │ │ ├── hint.cpp │ │ ├── serial.cpp │ │ └── solution.cpp │ └── ex03 │ │ ├── hint.cpp │ │ └── solution.cpp │ └── twoexercises │ ├── 05-stl-templates │ ├── 00-examples │ │ ├── 01-compilation-unit │ │ │ ├── Makefile │ │ │ ├── main.cpp │ │ │ ├── sum.cpp │ │ │ └── sum.hpp │ │ ├── 02-compilation-unit │ │ │ ├── Makefile │ │ │ ├── main.cpp │ │ │ ├── sum.cpp │ │ │ └── sum.hpp │ │ ├── 03-compilation-unit │ │ │ ├── Makefile │ │ │ ├── main.cpp │ │ │ ├── sum.cpp │ │ │ └── sum.hpp │ │ ├── 04-compilation-unit │ │ │ ├── Makefile │ │ │ ├── main.cpp │ │ │ ├── sum.cpp │ │ │ └── sum.hpp │ │ ├── 05-inheritance │ │ │ ├── ex01.cpp │ │ │ └── ex02.cpp │ │ └── compileWithPreProc.png │ ├── 01-sparse-matrix │ │ ├── Makefile │ │ ├── main.cpp │ │ ├── sparse_matrix.cpp │ │ └── sparse_matrix.hpp │ ├── 02-derivatives │ │ ├── Derivatives.hpp │ │ ├── Makefile │ │ └── main.cpp │ └── doc │ │ ├── 05-stl-templates.pdf │ │ └── 05-stl-templates.tex │ └── 06-eigen-newton │ ├── doc │ ├── 06-eigen-newton.pdf │ ├── 06-eigen-newton.tex │ ├── IntroToEigen.pdf │ └── ipe │ │ ├── class.eps │ │ ├── class.pdf │ │ ├── diagram.ipe │ │ ├── diagramNewton.ipe │ │ ├── newton.eps │ │ └── newton.pdf │ ├── example │ └── main.cpp │ └── src │ ├── Jacobian.cpp │ ├── Jacobian.hpp │ ├── JacobianFactory.hpp │ ├── Makefile │ ├── Newton.cpp │ ├── Newton.hpp │ ├── NewtonMethodsSupport.hpp │ ├── NewtonTraits.hpp │ └── main.cpp └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Mac DStore 5 | *.DS_Store 6 | 7 | # Compiled Object files 8 | *.slo 9 | *.lo 10 | *.o 11 | *.obj 12 | 13 | # Precompiled Headers 14 | *.gch 15 | *.pch 16 | 17 | # Compiled Dynamic libraries 18 | *.so 19 | *.dylib 20 | *.dll 21 | 22 | # Fortran module files 23 | *.mod 24 | *.smod 25 | 26 | # Compiled Static libraries 27 | *.lai 28 | *.la 29 | *.a 30 | *.lib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | 37 | # Latex 38 | *.snm 39 | *.nav 40 | *.log 41 | *.aux 42 | *.toc 43 | *.vrb 44 | 45 | # IDEs 46 | .vscode 47 | 48 | -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex01/hint/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | /*fill here*/ pow_recursive(/*fill here*/) { 8 | /*fill here*/ 9 | } 10 | 11 | template 12 | /*fill here*/ pow_iterative(/*fill here*/) { 13 | /*fill here*/ 14 | } 15 | 16 | double rel_error(double a, double b) { 17 | if (a == 0 && b == 0) 18 | return 0; 19 | return std::abs(a - b) / std::max(std::abs(a), std::abs(b)); 20 | } 21 | 22 | void test_error(double err, double toll) { 23 | std::cout << err << " " << (err < toll ? "PASSED" : "FAIL") << std::endl; 24 | } 25 | 26 | int main() { 27 | const std::vector exponents = { 0, 1, 2, 3, 5, 7, 8, 13 }; 28 | const std::vector base_uint = { 0, 1, 2, 3, 5, 7, 8, 13 }; 29 | const std::vector base_double = { 0, 1, 2.3, 3.7, 5.4, 7.9, 8.1 }; 30 | 31 | std::cout << "-- unsigned tests -----------------" << std::endl; 32 | for (const auto& e : exponents) { 33 | for (const auto& b : base_uint) { 34 | std::cout << pow_recursive(b, e) - std::pow(b, e) << std::endl; 35 | std::cout << pow_iterative(b, e) - std::pow(b, e) << std::endl; 36 | } 37 | } 38 | 39 | std::cout << "-- double tests -----------------" << std::endl; 40 | constexpr auto TOLL = std::numeric_limits::epsilon() * 10; 41 | std::cout << std::scientific << std::setprecision(6); 42 | for (const auto& e : exponents) { 43 | for (const auto& b : base_double) { 44 | test_error(rel_error(pow_recursive(b, e), std::pow(b, e)), TOLL); 45 | test_error(rel_error(pow_iterative(b, e), std::pow(b, e)), TOLL); 46 | } 47 | } 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex01/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | template 7 | T pow_recursive(const T& base, unsigned int exp) { 8 | if (exp == 0) 9 | return 1; 10 | else if (exp % 2) 11 | return base * pow_recursive(base * base, (exp - 1) / 2); 12 | else 13 | return pow_recursive(base * base, exp / 2); 14 | } 15 | 16 | template 17 | T pow_iterative(T base, unsigned int exp) { 18 | T res = 1; 19 | while (exp > 0) { 20 | if (exp & 1) 21 | res *= base; 22 | base *= base; 23 | exp >>= 1; 24 | } 25 | return res; 26 | } 27 | 28 | double rel_error(double a, double b) { 29 | if (a == 0 && b == 0) 30 | return 0; 31 | return std::abs(a - b) / std::max(std::abs(a), std::abs(b)); 32 | } 33 | 34 | void test_error(double err, double toll) { 35 | std::cout << err << " " << (err < toll ? "PASSED" : "FAIL") << std::endl; 36 | } 37 | 38 | int main() { 39 | const std::vector exponents = { 0, 1, 2, 3, 5, 7, 8, 13 }; 40 | const std::vector base_uint = { 0, 1, 2, 3, 5, 7, 8, 13 }; 41 | const std::vector base_double = { 0, 1, 2.3, 3.7, 5.4, 7.9, 8.1 }; 42 | 43 | std::cout << "-- unsigned tests -----------------" << std::endl; 44 | for (const auto& e : exponents) { 45 | for (const auto& b : base_uint) { 46 | std::cout << pow_recursive(b, e) - std::pow(b, e) << std::endl; 47 | std::cout << pow_iterative(b, e) - std::pow(b, e) << std::endl; 48 | } 49 | } 50 | 51 | std::cout << "-- double tests -----------------" << std::endl; 52 | constexpr auto TOLL = std::numeric_limits::epsilon() * 10; 53 | std::cout << std::scientific << std::setprecision(6); 54 | for (const auto& e : exponents) { 55 | for (const auto& b : base_double) { 56 | test_error(rel_error(pow_recursive(b, e), std::pow(b, e)), TOLL); 57 | test_error(rel_error(pow_iterative(b, e), std::pow(b, e)), TOLL); 58 | } 59 | } 60 | 61 | return 0; 62 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex02/hint/horner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HORNER_H 2 | #define HORNER_H 3 | 4 | using eval_method_t = /*define the type of eval_method as a function pointer, this is c++11 style*/; 5 | 6 | /*fill here*/ evaluate_poly(/*fill here*/) { 7 | /*fill here*/ 8 | } 9 | 10 | inline double pow_integer(/*fill here*/) { 11 | /*can use ex01 code*/ 12 | } 13 | 14 | double eval(/*fill here*/) { 15 | /*fill here*/ 16 | } 17 | 18 | double eval_horner(/*fill here*/) { 19 | /*fill here*/ 20 | } 21 | 22 | #endif /* HORNER_H */ -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex02/hint/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "horner.hpp" 7 | 8 | int main() { 9 | std::cout << "Polynomial degree" << std::endl; 10 | std::cout << "=> "; 11 | /*get the polynomial degree in a unsigned int variable from standart input*/ 12 | 13 | std::cout << "Coefficients are computed automatically" << std::endl; 14 | /*fill vector using 2sin(2k) formula for coefficients*/ 15 | 16 | // some static parameters 17 | const double x_0 = 0.00; 18 | const double x_f = 1.00; 19 | const unsigned int n = 100000; 20 | const double h = (x_f - x_0) / (n - 1); 21 | 22 | // compute evaluation points an put them in a vector 23 | /*fill with code*/ 24 | 25 | std::cout << "Computing " << n << " evaluations of polynomial" 26 | << " with standard formula" << std::endl; 27 | { 28 | // we use the namespace in a limited scope thanks to the curly brackets 29 | using namespace std::chrono; 30 | // get current time using 'high_resolution_clock::now()' 31 | /*fill with code*/ 32 | evaluate_poly(points, coeff, eval); 33 | /*fill with code*/ 34 | // cast to milliseconds with 'duration_cast' 35 | // convert to integer with '.count()' method 36 | const auto dt = /*fill with code*/; 37 | std::cout << "Elapsed: " << dt << " [ms]" << std::endl; 38 | } 39 | 40 | std::cout << "Computing " << n << " evaluations of polynomial" 41 | << " with horner formula" << std::endl; 42 | { 43 | using namespace std::chrono; 44 | /*get current time*/ 45 | evaluate_poly(points, coeff, eval_horner); 46 | /*compute elapsed time as before*/ 47 | 48 | } 49 | 50 | return 0; 51 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex02/solution-advanced/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "horner.hpp" 7 | 8 | int main() { 9 | // parse parameter from file and print them 10 | const auto parameters = parse_parameters(get_file_contents("params.dat")); 11 | std::cout << "Parsed parameter are:" << std::endl; 12 | for (const auto& [key, value] : parameters) 13 | std::cout << "-- " << key << ": " << value << std::endl; 14 | 15 | // save parameters locally for ease of use (optional) 16 | const auto x0 = parameters.at("x_0"); 17 | const auto xf = parameters.at("x_f"); 18 | const auto n = static_cast(parameters.at("n_points")); 19 | const auto degree = static_cast(parameters.at("degree")); 20 | const auto h = (xf - x0) / (n - 1); 21 | 22 | // construct vector of points and coefficients with ranges 23 | const auto x_range = std::ranges::views::iota(0u, n) | 24 | std::ranges::views::transform([=](auto n) 25 | { return x0 + n * h; }); 26 | const std::vector points(x_range.begin(), x_range.end()); 27 | 28 | const auto coeff_range = std::ranges::views::iota(0u, degree + 1) | 29 | std::ranges::views::transform([=](auto n) 30 | { return 2 * std::sin(2.0 * n); }); 31 | const std::vector coeff(coeff_range.begin(), coeff_range.end()); 32 | 33 | // define the tests we are going to perform and loop over them 34 | const std::unordered_map TESTS = { 35 | {"standard std::pow", eval_std}, 36 | {"standard integer pow", eval_pow_integer}, 37 | {"standard pow by squaring", eval_squaring}, 38 | {"standard pow branchless squaring", eval_branchless}, 39 | {"horner", eval_horner}, 40 | }; 41 | for (const auto parallel_policy : { true, false }) { 42 | std::cout << "--------------------------------------" << std::endl; 43 | std::cout << " parallel execution: " << (parallel_policy ? "ON" : "OFF") << std::endl; 44 | std::cout << "--------------------------------------" << std::endl; 45 | for (const auto& [test_name, test_func] : TESTS) { 46 | std::cout << "Computing " << n << " evaluations of polynomial" 47 | << " with " << test_name << " formula" << std::endl; 48 | const auto dt = timeit([&]() 49 | { evaluate_poly(points, coeff, test_func, parallel_policy); }); 50 | std::cout << "Elapsed: " << dt << " [ms]" << std::endl; 51 | } 52 | } 53 | 54 | 55 | return 0; 56 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex02/solution-advanced/params.dat: -------------------------------------------------------------------------------- 1 | degree=1000 2 | n_points=10000 3 | x_0=0.0 4 | x_f=1.0 -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex02/solution/horner.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HORNER_H 2 | #define HORNER_H 3 | 4 | using eval_method_t = double (*)(const std::vector&, const double&); 5 | 6 | std::vector evaluate_poly(const std::vector& points, 7 | const std::vector& a, 8 | eval_method_t method) { 9 | std::vector result; 10 | result.reserve(points.size()); 11 | for (std::size_t i = 0; i < points.size(); ++i) 12 | result.push_back(method(a, points[i])); 13 | return result; 14 | } 15 | 16 | inline double pow_integer(const double& x, const unsigned int& n) { 17 | double result{ x }; 18 | 19 | for (unsigned int i = 2; i != n + 1; ++i) 20 | result *= x; 21 | 22 | return result; 23 | } 24 | 25 | double eval(const std::vector& a, const double& x) { 26 | double result{ a[0] }; 27 | for (std::vector::size_type k = 1; k < a.size(); ++k) { 28 | result += a[k] * std::pow(x, k); 29 | } 30 | return result; 31 | } 32 | 33 | double eval_horner(const std::vector& a, const double& x) { 34 | double result{ a.back() }; 35 | for (auto i = a.crbegin() + 1; i != a.crend(); ++i) 36 | result = result * x + (*i); 37 | 38 | return result; 39 | } 40 | 41 | #endif /* HORNER_H */ -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex02/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "horner.hpp" 7 | 8 | int main() { 9 | unsigned int degree; 10 | std::cout << "Polynomial degree" << std::endl; 11 | std::cout << "=> "; 12 | std::cin >> degree; 13 | 14 | std::vector coeff(degree + 1); 15 | std::cout << "Coefficients are computed automatically" << std::endl; 16 | for (unsigned int i = 0; i <= degree; ++i) 17 | coeff[i] = 2 * std::sin(2.0 * i); 18 | 19 | const double x_0 = 0.00; 20 | const double x_f = 1.00; 21 | const unsigned int n = 100000; 22 | const double h = (x_f - x_0) / (n - 1); 23 | 24 | std::vector points(n + 1); 25 | for (unsigned int i = 0; i <= n; ++i) 26 | points[i] = x_0 + i * h; 27 | 28 | std::cout << "Computing " << n << " evaluations of polynomial" 29 | << " with standard formula" << std::endl; 30 | { 31 | using namespace std::chrono; 32 | const auto t0 = high_resolution_clock::now(); 33 | evaluate_poly(points, coeff, eval); 34 | const auto t1 = high_resolution_clock::now(); 35 | const auto dt = duration_cast(t1 - t0).count(); 36 | std::cout << "Elapsed: " << dt << " [ms]" << std::endl; 37 | } 38 | 39 | std::cout << "Computing " << n << " evaluations of polynomial" 40 | << " with horner formula" << std::endl; 41 | { 42 | using namespace std::chrono; 43 | const auto t0 = high_resolution_clock::now(); 44 | evaluate_poly(points, coeff, eval_horner); 45 | const auto t1 = high_resolution_clock::now(); 46 | const auto dt = duration_cast(t1 - t0).count(); 47 | std::cout << "Elapsed: " << dt << " [ms]" << std::endl; 48 | } 49 | 50 | return 0; 51 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab01/ex03/smaller.cpp: -------------------------------------------------------------------------------- 1 | int smaller_standard(int a, int b) { 2 | if(a < b) 3 | return a; 4 | else 5 | return b; 6 | } 7 | 8 | int smaller_branchless(int a, int b) { 9 | return a*(a 2 | #include 3 | #include 4 | #include 5 | 6 | void toupper_standard(char* d, int len) { 7 | for (int i = 0; i < len; ++i) { 8 | if (d[i] >= 'a' && d[i] <= 'z') 9 | d[i] -= 32; 10 | } 11 | } 12 | 13 | void toupper_branchless(char* d, int len) { 14 | for (int i = 0; i < len; ++i) { 15 | d[i] -= 32 * (d[i] >= 'a' && d[i] <= 'z'); 16 | } 17 | } 18 | 19 | static constexpr char alphanum[] = 20 | "0123456789" 21 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 22 | "abcdefghijklmnopqrstuvwxyz"; 23 | 24 | int main() { 25 | std::random_device r; 26 | std::mt19937 engine(r()); 27 | 28 | const unsigned int num_test = 100000; 29 | std::uniform_int_distribution<> len_distrib(5, 50); 30 | std::uniform_int_distribution<> chr_distrib(0, 62); // all alphanum ascii characters 31 | 32 | // build tests 33 | std::vector> tests; 34 | tests.reserve(num_test); 35 | for (unsigned int i = 0; i < num_test; ++i) { 36 | std::vector tmp; 37 | const auto len = len_distrib(engine); 38 | tmp.reserve(len); 39 | for (int i = 0; i < len; ++i) { 40 | tmp.push_back(alphanum[chr_distrib(engine)]); 41 | } 42 | tests.push_back(tmp); 43 | } 44 | 45 | // run tests 46 | for (const auto& func : { toupper_branchless, toupper_standard }) { 47 | using namespace std::chrono; 48 | const auto t0 = high_resolution_clock::now(); 49 | for (auto t : tests) 50 | func(t.data(), t.size()); 51 | const auto t1 = high_resolution_clock::now(); 52 | const auto dt = duration_cast(t1 - t0).count(); 53 | std::cout << "Elapsed: " << dt << " [μs]" << std::endl; 54 | } 55 | 56 | return 0; 57 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab02/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 - Copy elision 2 | Under some circumstances, thank to copy elision, the compiler can optimize away a copy in 3 | - a return statement 4 | - during the initialization of an object 5 | 6 | This is important because, among other things, it allows to write more readable code. Indeed, suppose you have a `HugeDynamicObject` that requires memory allocation on the heap. Suppose you want to write a function `parametrically_build_object` that creates `HugeDynamicObject` depending on some some parameters `p1, p2`. Without copy elision a function with the signature 7 | ```cpp 8 | HugeDynamicObject parametrically_build_object(double p1, double p2) { 9 | //... some code 10 | return HugeDynamicObject{/**/}; 11 | } 12 | HugeDynamicObject hdo = parametrically_build_object(1.0, 2.5); 13 | ``` 14 | would be very wasteful since it requires to copy the result of the function into `h`. It would be better something like: 15 | ```cpp 16 | void parametrically_change_object(double p1, double p2, HugeDynamicObject &hdo) { 17 | //... modify hdo as you wish 18 | } 19 | HugeDynamicObject hdo; 20 | parametrically_change_object(1.0, 2.5, hdo); 21 | ``` 22 | which is less readable, since you cannot immediately understand which are the inputs and which are the outputs. 23 | 24 | ## Assignments 25 | 1. Starting from the `hint` folder, implement a class `Noisy` that has a constructor, a copy constructor and a destructor which just print to the standard output that they have been called. 26 | 2. Compile `main.cpp` and try to understand where copy elision is applied. 27 | 28 | # Exercise 2 - Polymorphism 29 | ## Assignments 30 | 1. Implement an abstract class `Shape`, that has a name and a pure virtual function `getArea` to compute its area 31 | 2. Implement the concrete children `Circle` and `Rectangle` that receive as constructor parameters the values needed to compute the area (e.g. radius, basis, height...) and override the pure virtual `getArea`. 32 | 3. Instantiate a vector of `Shape`s using `shared_ptr` and print the area of each shape 33 | 34 | # Exercise 3 - Newton's method 35 | Newton's method is a numerical algorithm used to find roots of a function iteratively. If the function satisfies sufficient regularity assumptions and the initial guess $x_0$ is close to the root, then 36 | 37 | $$x_{n+1}=x_{n}-{\frac {f(x_{n})}{f'(x_{n})}}$$ 38 | 39 | ## Assignments 40 | Write a `NewtonSolver` class that takes as constructor arguments 41 | - the function $f(x)$ 42 | - its derivative $f'(x)$ 43 | - the maximum number of iterations `max_iter` 44 | - the tolerance on the residual `rtol` 45 | - the tolerance on the step size `stol` 46 | 47 | Has a method `solve` that takes as input the starting point $x_0$, and has the following members (accessible with a getter): 48 | - `iter` number of iterations 49 | - `res` residual 50 | - `xs` the history of the iterations 51 | 52 | Apply it to the function $f(x) = x^2 - 2$. Pass it as: 53 | - a function pointer 54 | - a lambda 55 | 56 | -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex01/hint/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Noisy { 4 | public: 5 | // define constructor 6 | // define copy constructor 7 | // define destructor 8 | }; 9 | 10 | // ============================================================================ 11 | // DO NOT TOUCH FROM HERE 12 | // just read and interpter what is happening 13 | // ============================================================================ 14 | 15 | Noisy f() { 16 | Noisy v = Noisy(); 17 | return v; 18 | } 19 | 20 | void g(Noisy arg) { 21 | std::cout << "&arg = " << &arg << '\n'; 22 | } 23 | 24 | int main() { 25 | Noisy v = f(); 26 | std::cout << "&v = " << &v << '\n'; 27 | g(f()); 28 | return 0; 29 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex01/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | class Noisy { 4 | public: 5 | Noisy() { std::cout << "constructed at " << this << '\n'; } 6 | Noisy(const Noisy&) { std::cout << "copy-constructed\n"; } 7 | Noisy(Noisy&&) { std::cout << "move-constructed\n"; } 8 | ~Noisy() { std::cout << "destructed at " << this << '\n'; } 9 | }; 10 | 11 | Noisy f() { 12 | Noisy v = Noisy(); // copy elision when initializing v 13 | // from a temporary (until C++17) / prvalue (since C++17) 14 | return v; // NRVO from v to the result object (not guaranteed, even in C++17) 15 | } // if optimization is disabled, the move constructor is called 16 | 17 | void g(Noisy arg) { 18 | std::cout << "&arg = " << &arg << '\n'; 19 | } 20 | 21 | int main() { 22 | Noisy v = f(); // copy elision in initialization of v 23 | // from the temporary returned by f() (until C++17) 24 | // from the prvalue f() (since C++17) 25 | 26 | std::cout << "&v = " << &v << '\n'; 27 | 28 | g(f()); // copy elision in initialization of the parameter of g() 29 | // from the temporary returned by f() (until C++17) 30 | // from the prvalue f() (since C++17) 31 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex02/hint/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Shape { 9 | public: 10 | // constructor that initializes the name 11 | // pure virtual getter for the area 12 | // getter for the shape name (non virtual) 13 | // virtual destructor 14 | private: 15 | // member with the name of the shape (const) 16 | }; 17 | 18 | // Implement the classes "Circle" and "Rectangle" 19 | // The constructor must be empty, in the sense the name of the shape 20 | // should not be a user's choice 21 | 22 | 23 | int main() { 24 | // Instantiate vector of shapes 25 | // Add some shapes 26 | // Loop over shapes and print 27 | return 0; 28 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex02/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Shape { 9 | public: 10 | Shape(const std::string& name) : m_name(name) {}; 11 | virtual double getArea() = 0; 12 | const std::string& getName() const { return m_name; }; 13 | // always make base classes' destructors virtual when they're meant to be manipulated polymorphically. 14 | virtual ~Shape() = default; 15 | private: 16 | const std::string m_name; 17 | }; 18 | 19 | class Circle : public Shape { 20 | public: 21 | Circle(double radius) : Shape("Circle"), m_radius(radius) {}; 22 | virtual double getArea() override { return m_radius * m_radius * std::numbers::pi_v; }; 23 | virtual ~Circle() override = default; 24 | private: 25 | const double m_radius; 26 | }; 27 | 28 | class Rectangle : public Shape { 29 | public: 30 | Rectangle(double b, double h) : Shape("Rectangle"), m_basis(b), m_height(h) {}; 31 | virtual double getArea() override { return m_basis * m_height; }; 32 | virtual ~Rectangle() override = default; 33 | private: 34 | const double m_basis, m_height; 35 | }; 36 | 37 | int main() { 38 | std::vector> shapes; 39 | shapes.push_back(std::make_shared(1.0)); 40 | shapes.push_back(std::make_shared(2.5, 0.2)); 41 | 42 | // notice the use of the auto range 43 | for (const auto& s : shapes) { 44 | std::cout << "I am a " << s->getName() << ", my area is: " << s->getArea() << std::endl; 45 | } 46 | return 0; 47 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex03/hint/main.cpp: -------------------------------------------------------------------------------- 1 | #include "newton.hpp" 2 | 3 | int main() { 4 | NewtonSolver solver([](auto x) { return x * x - 2.0; }, [](auto x) { return 2.0 * x; }); 5 | solver.solve(1.0); 6 | 7 | std::cout << "x = " << solver.getResult() << std::endl; 8 | std::cout << "r = " << solver.getResidual() << std::endl; 9 | std::cout << "dx = " << solver.getStep() << std::endl; 10 | std::cout << "iter = " << solver.getIter() << std::endl; 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex03/hint/newton.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NEWTON_H 2 | #define NEWTON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class NewtonSolver { 11 | public: 12 | NewtonSolver( 13 | // function f 14 | // derivative of f 15 | // max iterations 16 | // residual tolerance, by default 2 machine epsilon 17 | // step tolerance, by default 2 machine epsilon 18 | ) 19 | : 20 | /*initialize members*/ { 21 | } 22 | 23 | 24 | // ================================================ 25 | // implement here the public method "solve" that recives as input x_0 26 | // has the following pseudocode 27 | // * reset the history of solutions x_n 28 | // * while not reached max number of iterations 29 | // ** compute residual at current x_iter 30 | // ** if residual too small break 31 | // ** compute derivative at current x_iter 32 | // ** apply newton step 33 | // ** if step is smaller than tolerance break 34 | 35 | // ================================================ 36 | // define here the public "getters" for 37 | // * the result 38 | // * the last step size 39 | // * the history of x_n (return a const reference) 40 | // * the last residual 41 | // * the number of iterations performed 42 | 43 | // ================================================ 44 | // define here the private members, you need 45 | // * the function f 46 | // * the derivative of f 47 | // * max number of iterations 48 | // * the tolerance on the residual 49 | // * the tolerance of the step size 50 | // * the history of the iterations x_n 51 | // * the last evaluation of f 52 | // * the last evaluation of the derivative of f 53 | // * the current residual 54 | // * the current number of iterations 55 | }; 56 | 57 | #endif /* NEWTON_H */ -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex03/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include "newton.hpp" 2 | 3 | int main() { 4 | NewtonSolver solver([](auto x) { return x * x - 2.0; }, [](auto x) { return 2.0 * x; }); 5 | solver.solve(1.0); 6 | 7 | std::cout << "x = " << solver.getResult() << std::endl; 8 | std::cout << "r = " << solver.getResidual() << std::endl; 9 | std::cout << "dx = " << solver.getStep() << std::endl; 10 | std::cout << "iter = " << solver.getIter() << std::endl; 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab02/ex03/solution/newton.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NEWTON_H 2 | #define NEWTON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class NewtonSolver { 11 | public: 12 | NewtonSolver( 13 | const std::function& fun_, 14 | const std::function& dfun_, 15 | const unsigned int n_max_it_ = 100, 16 | const double tol_fun_ = std::numeric_limits::epsilon(), 17 | const double tol_x_ = std::numeric_limits::epsilon() 18 | ) 19 | : 20 | fun(fun_), 21 | dfun(dfun_), 22 | n_max_it(n_max_it_), 23 | tol_fun(tol_fun_), 24 | tol_x(tol_x_), 25 | x(0), 26 | df_dx(0), 27 | dx(0), 28 | res(0), 29 | iter(0) { 30 | } 31 | 32 | void solve(const double x0) { 33 | x.resize(1); 34 | x.reserve(n_max_it + 1); 35 | x[0] = x0; 36 | for (iter = 0; iter < n_max_it; ++iter) { 37 | res = fun(x[iter]); 38 | 39 | if (std::abs(res) < tol_fun) 40 | break; 41 | 42 | df_dx = dfun(x[iter]); 43 | 44 | dx = -res / df_dx; 45 | x.push_back(x[iter] + dx); 46 | 47 | if (std::abs(dx) < tol_x) 48 | break; 49 | } 50 | } 51 | 52 | double getResult() const { 53 | return x[x.size() - 1]; 54 | }; 55 | 56 | double getStep() const { 57 | return dx; 58 | }; 59 | 60 | const std::vector& getHistory() const { 61 | return x; 62 | } 63 | 64 | double getResidual() const { 65 | return res; 66 | }; 67 | 68 | unsigned int getIter() const { 69 | return iter; 70 | }; 71 | 72 | private: 73 | std::function fun; 74 | std::function dfun; 75 | 76 | const unsigned int n_max_it; 77 | const double tol_fun; 78 | const double tol_x; 79 | 80 | std::vector x; 81 | double df_dx; 82 | double dx; 83 | double res; 84 | unsigned int iter; 85 | }; 86 | 87 | #endif /* NEWTON_H */ -------------------------------------------------------------------------------- /Labs/2022-23/lab03/README.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 - Getting started with the STL 2 | Fill the code in `main.cpp` to complete four small assigment about the STL. 3 | The code will compare your solution against the correct one and tell you if it is correct. 4 | 5 | # Exericse 2 - Sparse Matrix 6 | A sparse matrix is a matrix in which most of the elements are zero. Sparse matrices are often used in numberical analysis, especially for finite element methods. 7 | 8 | ## Step 1 - The abstract class 9 | There are many different ways to implement as sparse matrix. In this laboratory we will show a couple of naive implementations that rely on the STL. For this reason our starting point is the definition of an abstract class where we declare all the methods that we want to have. Namely: 10 | 11 | * a getter for number of rows 12 | * a getter for number of cols 13 | * a getter for number of number of non-zero 14 | * a print function 15 | * a vmult method that implements vector multiplication 16 | * an access method to read and write the elements of the matrix 17 | 18 | 19 | ## Step 2 - Matrix made with maps 20 | After having defined our interface we can really implement our sparse matrix class. In this step the aim is to code a `MapMatrix` sparse matrix, in which each row is represented by a map and all the maps are collected in a `std::vector`. For debugging we suggest the following compiler flags 21 | ``` 22 | g++ hint.cpp -std=c++20 -O0 -Wall -Wextra -pedantic -fsanitize=address 23 | ``` 24 | Namely, the last flag will check that you do not exceed the container's memory bounds. 25 | 26 | ## Step 3 - Benchmark and test code 27 | Finally we want to implement some utilities to benchmark the performances of our class and test if the implementation is correct. Namely we implement: 28 | 29 | * a function to fill a matrix and given the final size $n$. Namely it puts on the main diagonal -2 and 1 on the diagonal above and below the main one 30 | * a function `bool eq(const Vector &lhs, const Vector &rhs)` that checks if two vectors are equal 31 | 32 | Then we will check that: 33 | * `matrix.print(std::cout)` effectively prints the expected tridiagonal matrix that we expect 34 | * $A [0, 1, 2, ..., n] = [1, 0, ..., 0, -n]$ 35 | 36 | Finnaly we print the elapsed time by each operation and confront what happens if instead of using a `std::map` we use `std::unordered_map`. 37 | 38 | ## Advanced - COO format 39 | Now we want to implement the more famous Coordinate list format. COO stores a list of (row, column, value) tuples. Ideally, the entries are sorted first by row index and then by column index, to improve random access times. To find the elements in a sorted vector exploit `std::lower_bound`. 40 | -------------------------------------------------------------------------------- /Labs/2022-23/lab03/ex02/step-1/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | // *instead of using double define a generic scalar type 'elem_t' for the elements of the matrix employing the c++ keyword 'using' 18 | // *similarly define the type Vector to be a 'std::vector' 19 | 20 | class SparseMatrix { 21 | public: 22 | // *constructor, takes no parameters but initializes: number of non-zero elements, number of rows and cols to zero 23 | // *getter for number of rows 24 | // *getter for number of cols 25 | // *getter for number of number of non-zero 26 | 27 | // *print function: prints the number of rows, cols and nnz; moreover calls 28 | // _print, the virtual version of print that is specialized in the children class 29 | 30 | 31 | // *abstract virtual method vmult that implements vector multiplication 32 | // *abstract virtual method operator()(size_t i, size_t j) that implements element access in read only (const version) 33 | // *abstract virtual method operator()(size_t i, size_t j) that implements element access in write (returns non-const reference) 34 | // *virtual destructor 35 | 36 | protected: // protected because need to be accessible to children! 37 | // *abstract virtual method _print that prints the matrix 38 | 39 | // *variable to store nnz 40 | // *variable to store number of rows 41 | // *variable to store number of cols 42 | }; 43 | 44 | int main() { 45 | return 0; 46 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab03/ex02/step-1/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using elem_t = double; 17 | using Vector = std::vector; 18 | 19 | class SparseMatrix { 20 | public: 21 | SparseMatrix() : m_nnz(0), m_nrows(0), m_ncols(0) {}; 22 | size_t nrows() const { return m_nrows; } 23 | size_t ncols() const { return m_ncols; } 24 | size_t nnz() const { return m_nnz; } 25 | 26 | void print(std::ostream& os = std::cout) const { 27 | os << "nrows: " << m_nrows << " | ncols:" << m_ncols << " | nnz: " << m_nnz << std::endl; 28 | _print(os); 29 | }; 30 | 31 | virtual Vector vmult(const Vector& v) const = 0; 32 | virtual const elem_t& operator()(size_t i, size_t j) const = 0; 33 | virtual elem_t& operator()(size_t i, size_t j) = 0; 34 | virtual ~SparseMatrix() = default; 35 | 36 | protected: 37 | virtual void _print(std::ostream& os) const = 0; 38 | size_t m_nnz; 39 | size_t m_nrows, m_ncols; 40 | }; 41 | 42 | int main() { 43 | return 0; 44 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab03/ex02/step-2/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using elem_t = double; 17 | using Vector = std::vector; 18 | 19 | class SparseMatrix { 20 | public: 21 | SparseMatrix() : m_nnz(0), m_nrows(0), m_ncols(0) {}; 22 | size_t nrows() const { return m_nrows; } 23 | size_t ncols() const { return m_ncols; } 24 | size_t nnz() const { return m_nnz; } 25 | 26 | void print(std::ostream& os) const { 27 | os << "nrows: " << m_nrows << " | ncols:" << m_ncols << " | nnz: " << m_nnz << std::endl; 28 | _print(os); 29 | }; 30 | 31 | virtual Vector vmult(const Vector& v) const = 0; 32 | virtual const elem_t& operator()(size_t i, size_t j) const = 0; 33 | virtual elem_t& operator()(size_t i, size_t j) = 0; 34 | virtual ~SparseMatrix() = default; 35 | 36 | protected: 37 | virtual void _print(std::ostream& os) const = 0; 38 | size_t m_nnz; 39 | size_t m_nrows, m_ncols; 40 | }; 41 | 42 | class MapMatrix : public SparseMatrix { 43 | public: 44 | virtual Vector vmult(const Vector& x) const override { 45 | // assert x.size() == m_ncols 46 | // allocate memory for result and initialize to 0 47 | // loop over each element of the matrix and add contribute to result 48 | } 49 | 50 | virtual double& operator()(size_t i, size_t j) override { 51 | // check if we have enough rows, if not add them 52 | // find column entry, if not present add it 53 | // return value reference 54 | } 55 | virtual const double& operator()(size_t i, size_t j) const override { 56 | // return value reference with no check, we use the c++ convention of no bounds check 57 | } 58 | virtual ~MapMatrix() override = default; 59 | protected: 60 | virtual void _print(std::ostream& os) const { 61 | // print the data 62 | } 63 | 64 | /*the data is stored in a vector of maps of type size_t, elem_t*/ m_data; 65 | }; 66 | 67 | int main() { 68 | MapMatrix m; 69 | m(0, 0) = 1; 70 | m(1, 1) = 1; 71 | m.print(std::cout); 72 | const auto x = m.vmult({{2, 3}}); 73 | std::cout << x[0] << " " << x[1] << std::endl; 74 | return 0; 75 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab03/ex02/step-2/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using elem_t = double; 17 | using Vector = std::vector; 18 | 19 | class SparseMatrix { 20 | public: 21 | SparseMatrix() : m_nnz(0), m_nrows(0), m_ncols(0) {}; 22 | size_t nrows() const { return m_nrows; } 23 | size_t ncols() const { return m_ncols; } 24 | size_t nnz() const { return m_nnz; } 25 | 26 | void print(std::ostream& os) const { 27 | os << "nrows: " << m_nrows << " | ncols:" << m_ncols << " | nnz: " << m_nnz << std::endl; 28 | _print(os); 29 | }; 30 | 31 | virtual Vector vmult(const Vector& v) const = 0; 32 | virtual const elem_t& operator()(size_t i, size_t j) const = 0; 33 | virtual elem_t& operator()(size_t i, size_t j) = 0; 34 | virtual ~SparseMatrix() = default; 35 | 36 | protected: 37 | virtual void _print(std::ostream& os) const = 0; 38 | size_t m_nnz; 39 | size_t m_nrows, m_ncols; 40 | }; 41 | 42 | class MapMatrix : public SparseMatrix { 43 | public: 44 | virtual Vector vmult(const Vector& x) const override { 45 | assert(x.size() == m_ncols); 46 | Vector res(x.size()); 47 | for (size_t i = 0; i < m_data.size(); ++i) { 48 | for (const auto& [j, v] : m_data[i]) { 49 | res[i] += x[j] * v; 50 | } 51 | } 52 | return res; 53 | } 54 | 55 | virtual double& operator()(size_t i, size_t j) override { 56 | if (m_data.size() < i + 1) { 57 | m_data.resize(i + 1); 58 | m_nrows = i + 1; 59 | } 60 | const auto it = m_data[i].find(j); 61 | if (it == m_data[i].end()) { 62 | m_ncols = std::max(m_ncols, j + 1); 63 | m_nnz++; 64 | return (*m_data[i].emplace(j, 0).first).second; 65 | } 66 | return (*it).second; 67 | } 68 | virtual const double& operator()(size_t i, size_t j) const override { 69 | return m_data[i].at(j); 70 | } 71 | virtual ~MapMatrix() override = default; 72 | protected: 73 | virtual void _print(std::ostream& os) const { 74 | for (size_t i = 0; i < m_data.size(); ++i) { 75 | for (const auto& [j, v] : m_data[i]) 76 | os << i << "," << j << "," << v << std::endl; 77 | } 78 | } 79 | 80 | private: 81 | std::vector> m_data; 82 | }; 83 | 84 | int main() { 85 | MapMatrix m; 86 | m(0, 0) = 1; 87 | m(1, 1) = 1; 88 | m.print(std::cout); 89 | const auto x = m.vmult({{2, 3}}); 90 | std::cout << x[0] << " " << x[1] << std::endl; 91 | return 0; 92 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex01/helloworld.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | // Initialize the MPI environment 7 | MPI_Init(&argc, &argv); 8 | 9 | // Get the number of processes and their rank 10 | int world_size, world_rank; 11 | MPI_Comm_size(MPI_COMM_WORLD, &world_size); 12 | MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); 13 | 14 | // Get the name of the processor 15 | char processor_name[MPI_MAX_PROCESSOR_NAME]; 16 | int name_len; 17 | MPI_Get_processor_name(processor_name, &name_len); 18 | 19 | // Print off a hello world message 20 | std::cout << "Hello world from processor "<< processor_name 21 | << ", rank " << world_rank 22 | << " out of " << world_size 23 | << " processors" << std::endl; 24 | 25 | // Finalize the MPI environment. 26 | MPI_Finalize(); 27 | return 0; 28 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex01/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | MPI_Init(&argc, &argv); 9 | int rank, size; 10 | MPI_Comm_size(MPI_COMM_WORLD, &size); 11 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 12 | 13 | const std::string greeting = 14 | /*more than one argument*/ ? /*second string passed with argv*/ : /*default greeting*/; 15 | const std::string message = /*assemble the message*/; 16 | 17 | if (rank) { 18 | // send your local message size and then message to processor 0 19 | } else { // you are process 0 20 | // output your message 21 | // receive all the messages from the other processors and output them 22 | } 23 | 24 | MPI_Finalize(); 25 | return 0; 26 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex01/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | MPI_Init(&argc, &argv); 9 | int rank, size; 10 | MPI_Comm_size(MPI_COMM_WORLD, &size); 11 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 12 | 13 | const std::string greeting = (argc > 1) ? argv[1] : "world"; 14 | const std::string message = "Hello "+ greeting +", from rank " + 15 | std::to_string(rank) + " of " + 16 | std::to_string(size); 17 | 18 | if (rank > 0) { 19 | const unsigned length = unsigned(message.size()); 20 | MPI_Send(&length, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD); 21 | MPI_Send(&message[0], length, MPI_CHAR, 0, 1, MPI_COMM_WORLD); 22 | } else { 23 | std::cout << message << std::endl; 24 | std::string recv_message; 25 | unsigned recv_length; 26 | for (int r = 1; r < size; r++) { 27 | MPI_Recv(&recv_length, 1, MPI_UNSIGNED, r, 0, MPI_COMM_WORLD, 28 | MPI_STATUS_IGNORE); 29 | recv_message.resize(recv_length); 30 | MPI_Recv(&recv_message[0], recv_length, MPI_CHAR, r, 1, MPI_COMM_WORLD, 31 | MPI_STATUS_IGNORE); 32 | std::cout << recv_message << std::endl; 33 | } 34 | } 35 | 36 | MPI_Finalize(); 37 | return 0; 38 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex02/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | template 11 | std::array compute_gradient( 12 | const std::function &)> &f, 13 | const std::array & x, 14 | double h) { 15 | // initialize gradient 16 | // FILL HERE 17 | 18 | // get rank and size 19 | // FILL HERE 20 | 21 | // subdivide computations by processor 22 | for (/*FILL HERE*/) { 23 | auto x_plus_h (x), x_minus_h (x); 24 | x_plus_h[i] += h; 25 | x_minus_h[i] -= h; 26 | gradient[i] = (f (x_plus_h) - f (x_minus_h)) / 2.0 / h; 27 | } 28 | 29 | 30 | // send result to main proc 31 | for (size_t i = 0; i < N; ++i) { // for each component of the gradient 32 | const int belongs_to_proc = /*FILL HERE: compute to which proc it belongs to*/; 33 | if(belongs_to_proc /*!= 0*/) { // then we need to send it to proc 0 34 | if(/*FILL HERE: belong to us*/){ 35 | // FILL HERE: we send it to proc 0 36 | } 37 | else if(/*FILL HERE: we are proc 0*/) { 38 | // FILL HERE: we receive the data 39 | } 40 | } 41 | } 42 | return gradient; 43 | } 44 | 45 | int main(int argc, char* argv[]) { 46 | // init MPI, get rank and size 47 | // FILL HERE 48 | 49 | // define types 50 | using Vector = std::array; 51 | using Function = std::function; 52 | // define the field f of which we compute the gradient at the point where we evaluate it 53 | const Function f = [](auto v) { 54 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 2*v[3]*v[3]*v[3]; 55 | }; 56 | const Vector y = {0, 1, std::numbers::pi, 2}; 57 | // define the correct value of the gradient 58 | const Vector g_true = {1, std::exp(1), 0, 24}; 59 | // compute the gradient with our function 60 | const Vector g = compute_gradient(f, y, 1e-8); 61 | 62 | // check if the solution is correct 63 | if(/*FILL HERE*/) { 64 | std::cout << std::setprecision(4) << std::scientific; 65 | for(size_t i = 0; i < g.size(); ++i) { 66 | const auto err = std::abs(g[i] - g_true[i]); 67 | std::cout << i << " | "<< g[i] << (err < 1e-6 ? " PASS" : " FAIL") << std::endl; 68 | } 69 | } 70 | // FILL HERE: finalize 71 | return 0; 72 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex02/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | template 10 | std::array compute_gradient( 11 | const std::function &)> &f, 12 | const std::array & x, 13 | double h) { 14 | std::array gradient; 15 | 16 | for (size_t i = 0; i < N; i++) { 17 | auto x_plus_h (x), x_minus_h (x); 18 | x_plus_h[i] += h; 19 | x_minus_h[i] -= h; 20 | gradient[i] = (f (x_plus_h) - f (x_minus_h)) / 2.0 / h; 21 | } 22 | return gradient; 23 | } 24 | 25 | int main() { 26 | // define types 27 | using Vector = std::array; 28 | using Function = std::function; 29 | 30 | // define the field f of which we compute the gradient at the point where we evaluate it 31 | const Function f = [](auto v) { 32 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 2*v[3]*v[3]*v[3]; 33 | }; 34 | const Vector y = {0, 1, std::numbers::pi, 2}; 35 | // define the correct value of the gradient 36 | const Vector g_true = {1, std::exp(1), 0, 24}; 37 | // compute the gradient with our function 38 | const Vector g = compute_gradient(f, y, 1e-8); 39 | 40 | // check if the result is correct 41 | std::cout << std::setprecision(4) << std::scientific; 42 | for(size_t i = 0; i < g.size(); ++i) { 43 | const auto err = std::abs(g[i] - g_true[i]); 44 | std::cout << i << " | "<< g[i] << (err < 1e-6 ? " PASS" : " FAIL") << std::endl; 45 | } 46 | 47 | return 0; 48 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex02/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | template 11 | std::array compute_gradient( 12 | const std::function &)> &f, 13 | const std::array & x, 14 | double h) { 15 | // initialize gradient 16 | std::array gradient; 17 | 18 | // get rank and size 19 | int rank, size; 20 | MPI_Comm_size(MPI_COMM_WORLD, &size); 21 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 22 | 23 | // subdivide computations by processor 24 | for (size_t i = rank; i < N; i += size) { 25 | auto x_plus_h (x), x_minus_h (x); 26 | x_plus_h[i] += h; 27 | x_minus_h[i] -= h; 28 | gradient[i] = (f (x_plus_h) - f (x_minus_h)) / 2.0 / h; 29 | } 30 | 31 | // broadcast result, each proc has all the gradient 32 | // for (size_t i = 0; i < gradient.size (); ++i) { 33 | // MPI_Bcast (&gradient[i], 1, MPI_DOUBLE, i % size, MPI_COMM_WORLD); 34 | // } 35 | 36 | // send result to main proc 37 | for (size_t i = 0; i < N; ++i) { 38 | const int belongs_to_proc = i % size; 39 | const int tag = i / size; 40 | if(belongs_to_proc /*!= 0*/) { 41 | if(rank == belongs_to_proc){ 42 | MPI_Send (&gradient[i], 1, MPI_DOUBLE, 0, tag, MPI_COMM_WORLD); 43 | } 44 | else if(rank == 0) { 45 | MPI_Recv (&gradient[i], 1, MPI_DOUBLE, belongs_to_proc, tag, MPI_COMM_WORLD, MPI_STATUS_IGNORE); 46 | } 47 | } 48 | } 49 | return gradient; 50 | } 51 | 52 | int main(int argc, char* argv[]) { 53 | // init MPI 54 | MPI_Init(&argc, &argv); 55 | int rank, size; 56 | MPI_Comm_size(MPI_COMM_WORLD, &size); 57 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 58 | 59 | 60 | // define types 61 | using Vector = std::array; 62 | using Function = std::function; 63 | // define the field f of which we compute the gradient at the point where we evaluate it 64 | const Function f = [](auto v) { 65 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 2*v[3]*v[3]*v[3]; 66 | }; 67 | const Vector y = {0, 1, std::numbers::pi, 2}; 68 | // define the correct value of the gradient 69 | const Vector g_true = {1, std::exp(1), 0, 24}; 70 | // compute the gradient with our function 71 | const Vector g = compute_gradient(f, y, 1e-8); 72 | 73 | // check if the solution is correct 74 | if(rank == 0) { 75 | std::cout << std::setprecision(4) << std::scientific; 76 | for(size_t i = 0; i < g.size(); ++i) { 77 | const auto err = std::abs(g[i] - g_true[i]); 78 | std::cout << i << " | "<< g[i] << (err < 1e-6 ? " PASS" : " FAIL") << std::endl; 79 | } 80 | } 81 | MPI_Finalize(); 82 | return 0; 83 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex03/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | 13 | auto timeit(const std::function& f) { 14 | using namespace std::chrono; 15 | const auto t0 = high_resolution_clock::now(); 16 | f(); 17 | const auto t1 = high_resolution_clock::now(); 18 | return duration_cast(t1 - t0).count(); 19 | } 20 | 21 | std::pair montecarlo(const std::function& f, 22 | unsigned long N) { 23 | // get rank and size 24 | // FILL HERE 25 | 26 | // broadcast the numer of samples to every proc 27 | if(/*FILL HERE: rank is not 0*/) { 28 | // FILL HERE: receive N 29 | } else { 30 | // FILL HERE: send N to each proc (that is not 0) 31 | } 32 | 33 | // compute how many samples this proc handles 34 | const auto subsample = /*FILL HERE*/; 35 | 36 | // initialize random engine 37 | const auto seed = /*FILL HERE: find a way to produce a different seed for each proc*/; 38 | std::mt19937 engine(seed); 39 | std::uniform_real_distribution dist(-1., 1.); 40 | 41 | // get samples 42 | double sum = 0., sum_sq = 0.; 43 | for (auto i = 0ul; i < subsample; ++i) { 44 | const auto fi = f(dist(engine)); 45 | sum += fi; 46 | sum_sq += fi * fi; 47 | } 48 | 49 | // send local mean and sum of squares to proc 0 50 | if(/*FILL HERE: rank is not 0*/) { 51 | // FILL HERE: send sum and sum_sq to proc 0 52 | } else { 53 | // FILL HERE: receive sum and sum_sq from each proc and sum them together 54 | } 55 | 56 | // FILL HERE: if rank is 0 make the computations, otherwise return {0, 0} 57 | } 58 | 59 | int main(int argc, char* argv[]) { 60 | // init MPI, get rank and size 61 | // FILL HERE 62 | 63 | // get number of samples to use from argv 64 | const size_t N = argc > 1 ? std::stoull(argv[1]) : 100000; 65 | // compute the integral and time execution time 66 | std::pair I; 67 | const auto dt = timeit([&]() { 68 | I = montecarlo([](auto x) { return std::sqrt(1 - x * x); }, N); 69 | }); 70 | // output results 71 | if (/*FILL HERE*/) { 72 | std::cout << "Elapsed: " << dt << " [ms]" << std::endl; 73 | std::cout << "Integral: " << I.first << std::endl; 74 | std::cout << "Error estimator: " << std::sqrt(I.second) << std::endl; 75 | std::cout << "Error: " << std::abs(I.first - std::numbers::pi / 2) << std::endl; 76 | } 77 | // Finalize MPI 78 | // FILL HERE 79 | return 0; 80 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab04/ex03/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | auto timeit(const std::function& f) { 11 | using namespace std::chrono; 12 | const auto t0 = high_resolution_clock::now(); 13 | f(); 14 | const auto t1 = high_resolution_clock::now(); 15 | return duration_cast(t1 - t0).count(); 16 | } 17 | 18 | std::pair montecarlo(const std::function& f, 19 | unsigned long N) { 20 | // initialize random engine 21 | std::mt19937 engine(0); 22 | std::uniform_real_distribution dist(-1., 1.); 23 | 24 | // get samples 25 | double sum = 0., sum_sq = 0.; 26 | for (auto i = 0ul; i < N; ++i) { 27 | const auto fi = f(dist(engine)); 28 | sum += fi; 29 | sum_sq += fi * fi; 30 | } 31 | 32 | // compute integral 33 | const auto domain_measure = 2.0; 34 | const double integral = domain_measure * sum / N; 35 | const double variance = (sum_sq - (sum * sum) / N) / (N - 1); 36 | const double integral_variance = domain_measure * domain_measure * variance / N; 37 | return { integral, integral_variance }; 38 | } 39 | 40 | int main(int argc, char* argv[]) { 41 | // get number of samples to use from argv 42 | const size_t N = argc > 1 ? std::stoull(argv[1]) : 100000; 43 | // compute the integral and time execution time 44 | std::pair I; 45 | const auto dt = timeit([&]() { 46 | I = montecarlo([](auto x) { return std::sqrt(1 - x * x); }, N); 47 | }); 48 | // output results 49 | std::cout << "Elapsed: " << dt << " [ms]" << std::endl; 50 | std::cout << "Integral: " << I.first << std::endl; 51 | std::cout << "Error estimator: " << std::sqrt(I.second) << std::endl; 52 | std::cout << "Error: " << std::abs(I.first - std::numbers::pi / 2) << std::endl; 53 | return 0; 54 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab05/ex02/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../utils.hpp" 4 | 5 | void power_method(Eigen::MatrixXd &A, Eigen::VectorXd &b, size_t max_iter, double toll) { 6 | double err = 1.0 + toll; 7 | size_t iter = 0; 8 | while(err > toll && iter < max_iter) { 9 | Eigen::VectorXd b_new = A * b; 10 | b_new /= b_new.norm(); 11 | err = (b_new - b).norm(); 12 | // instead of doing `b = b_new` which means we are doing a useless copy 13 | // we swap the two vectors since we do not need the old value of `b` anymore 14 | std::swap(b, b_new); 15 | iter++; 16 | } 17 | std::cout << "Iterations: " << iter << "\n"; 18 | std::cout << "Error: " << err << "\n"; 19 | } 20 | 21 | int main() { 22 | const unsigned long N = 4; 23 | Eigen::MatrixXd A = Eigen::MatrixXd::Zero(N, N); 24 | // Ideally choose a random vector to decrease the chance that our vector 25 | // is orthogonal to the eigenvector 26 | Eigen::VectorXd b = Eigen::VectorXd::Random(N); 27 | A.diagonal(-1).setConstant(-1.0); 28 | A.diagonal(0).setConstant(2.0); 29 | A.diagonal(1).setConstant(-1.0); 30 | 31 | const auto dt = timeit([&](){power_method(A, b, 100000, 1e-8);}); 32 | const auto eigenvalue = (b.transpose()*A*b) / (b.transpose()*b); 33 | std::cout << "Elapsed: " << dt << std::endl; 34 | std::cout << "Eigenvalue: " << eigenvalue << std::endl; 35 | std::cout << "Eigenvector: \n" << b << std::endl; 36 | 37 | // test out solution against Eigen 38 | Eigen::EigenSolver es(A); 39 | Eigen::Index maxRow; 40 | const auto test_eigenvalue = es.eigenvalues().real().maxCoeff(&maxRow); 41 | const auto test_eigenvector = es.eigenvectors().real().col(maxRow); 42 | std::cout << "The eigenvalue error against Eigen is: " << (eigenvalue(0, 0) - test_eigenvalue) << std::endl; 43 | std::cout << "The eigenvector error against Eigen is: " << (b - test_eigenvector).norm() << std::endl; 44 | 45 | return 0; 46 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab05/ex03/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "../utils.hpp" 8 | 9 | std::vector get_primes(unsigned long n) { 10 | int rank, size; 11 | MPI_Comm_size(MPI_COMM_WORLD, &size); 12 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 13 | 14 | // broadcast n 15 | // FILL HERE 16 | 17 | // calculate the local segment size 18 | // FILL HERE 19 | 20 | // calculate the first number of the local segment 21 | // FILL HERE 22 | 23 | // initialize the `is_prime` local vector 24 | // FILL HERE 25 | 26 | // initialize the current number we are considering (to 2 since it is the first prime) 27 | // FILL HERE 28 | while (/*the square of the current prime is smaller than `n`*/) { 29 | // find the index of the first number that is multiple of the current prime in our partition 30 | // if the square of the current prime is greater than the start of the current segment 31 | // we start directly from the square of the current prime, otherwise we have to check 32 | // the reminder of the first number of the local segment w.r.t. the current prime 33 | // and consequently find which is the first multiple of the current prime 34 | unsigned long first_to_remove; 35 | // FILL HERE 36 | 37 | // apply the sieve, i.e. from first_to_remove to the end of the segment 38 | // flag as false `is_prime` each multiple of the current prime 39 | for (/*FILL HERE*/) { 40 | // FILL HERE 41 | } 42 | 43 | // just rank 0 search for the next prime 44 | // we are assuming that smallest prime greater than sqrt(n) belongs to rank 0 45 | // since usually `size` << `n`, this is reasonable 46 | // FILL HERE 47 | 48 | // broadcast the prime number we have found to all proc 49 | // FILL HERE 50 | } 51 | 52 | // allocate buffer to collect `is_prime` segments at rank 0 53 | // FILL HERE 54 | 55 | // calculate counts and displacements for MPI_Gatherv of `is_prime` 56 | // FILL HERE 57 | 58 | // do the MPI_Gatherv 59 | // FILL HERE 60 | 61 | return is_prime_global; 62 | } 63 | 64 | int main(int argc, char* argv[]) { 65 | MPI_Init(&argc, &argv); 66 | int rank, size; 67 | MPI_Comm_size(MPI_COMM_WORLD, &size); 68 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 69 | 70 | std::vector is_prime; 71 | const auto dt = timeit([&]() { is_prime = get_primes(100); }); 72 | if (rank == 0) { 73 | std::cout << "Elapsed: " << dt << " [ms] " << std::endl; 74 | std::cout << is_prime << std::endl; 75 | } 76 | 77 | MPI_Finalize(); 78 | return 0; 79 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab05/ex03/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #include "../utils.hpp" 8 | 9 | std::vector get_primes(unsigned long n) { 10 | int rank, size; 11 | MPI_Comm_size(MPI_COMM_WORLD, &size); 12 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 13 | 14 | // broadcast n 15 | MPI_Bcast(&n, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); 16 | // calculate the local segment size 17 | const auto local_n = n / size + ((n % size) > (unsigned long)rank); 18 | // calculate the start of the segment 19 | const auto min = (n / size) * rank + std::min((unsigned long)rank, n % size); 20 | // initialize the `is_prime` vector 21 | std::vector is_prime(local_n, '1'); 22 | if (rank == 0) { 23 | is_prime[0] = is_prime[1] = '0'; 24 | } 25 | 26 | // initialize the current number we are considering (to 2 since it is the first prime) 27 | unsigned long curr = 2; 28 | while (curr * curr < n) { 29 | // find the index of the first number that is multiple of the current prime in our partition 30 | unsigned long first_to_remove; 31 | if (curr * curr > min) { 32 | first_to_remove = curr * curr - min; 33 | } else { 34 | first_to_remove = (curr - (min % curr)) * ((min % curr) > 0); 35 | } 36 | // apply the sieve, i.e. from first_to_remove to the end of the segment 37 | // flag as false `is_prime` each multiple of the current prime 38 | for (unsigned long i = first_to_remove; i < local_n; i += curr) { 39 | is_prime[i] = '0'; 40 | } 41 | // just rank 0 search for the next prime 42 | // we are assuming that smallest prime greater than sqrt(n) belongs to rank 0 43 | // since usually `size` << `n`, this is reasonable 44 | if (rank == 0) { 45 | while (!is_prime[curr++]) {}; 46 | } 47 | // broadcast the prime number we have found to all proc 48 | MPI_Bcast(&curr, 1, MPI_UNSIGNED_LONG, 0, MPI_COMM_WORLD); 49 | } 50 | 51 | // allocate buffer to collect at rank 0 52 | std::vector is_prime_global; 53 | // calculate counts and displacements for MPI_Gatherv 54 | std::vector counts, displs; 55 | if (rank == 0) { 56 | is_prime_global.resize(n); 57 | displs.push_back(0); 58 | counts.push_back(local_n); 59 | for(int i = 1; i < size; ++i) { 60 | displs.push_back(counts.back() + displs.back()); 61 | counts.push_back(n / size + (((int)n % size) > i)); 62 | } 63 | } 64 | // do the MPI_Gatherv 65 | MPI_Gatherv(&is_prime[0], is_prime.size(), MPI_CHAR, &is_prime_global[0], 66 | &counts[0], &displs[0], MPI_CHAR, 0, MPI_COMM_WORLD); 67 | return is_prime_global; 68 | } 69 | 70 | int main(int argc, char* argv[]) { 71 | MPI_Init(&argc, &argv); 72 | int rank, size; 73 | MPI_Comm_size(MPI_COMM_WORLD, &size); 74 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 75 | 76 | std::vector is_prime; 77 | const auto dt = timeit([&]() { is_prime = get_primes(100); }); 78 | if (rank == 0) { 79 | std::cout << "Elapsed: " << dt << " [ms] " << std::endl; 80 | std::cout << is_prime << std::endl; 81 | } 82 | 83 | MPI_Finalize(); 84 | return 0; 85 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab05/utils.hpp: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_H__ 2 | #define __UTILS_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | template 12 | std::ostream& operator<< (std::ostream& out, const std::vector& v) { 13 | if ( !v.empty() ) { 14 | out << '['; 15 | std::copy(v.begin(), v.end(), std::ostream_iterator(out, ", ")); 16 | out << "\b\b]"; // use two ANSI backspace characters '\b' to overwrite final ", " 17 | } 18 | return out; 19 | } 20 | 21 | auto timeit(const std::function& f) { 22 | using namespace std::chrono; 23 | const auto t0 = high_resolution_clock::now(); 24 | f(); 25 | const auto t1 = high_resolution_clock::now(); 26 | return duration_cast(t1 - t0).count(); 27 | } 28 | 29 | #endif -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex01/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int 4 | main(int argc, char** argv) 5 | { 6 | 7 | int id = 0; 8 | std::cout << "Hello World from thread " << id << std::endl; 9 | 10 | return 0; 11 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex01/solution_v1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | #pragma omp parallel 7 | { 8 | int thread_id = omp_get_thread_num(); 9 | std::cout << "Hello World from thread " << thread_id << "!" << std::endl; 10 | } 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex01/solution_v2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int 6 | main(int argc, char **argv) 7 | { 8 | int thread_id; 9 | int n_threads; 10 | 11 | /** 12 | * Fork a team of threads with each thread 13 | * having a private thread_id variable. 14 | */ 15 | #pragma omp parallel private(thread_id) shared(n_threads) 16 | { 17 | thread_id = omp_get_thread_num(); 18 | 19 | /** 20 | * The following block is "critical", i.e. we only let one thread 21 | * execute this block at a time. 22 | * 23 | * Other types of blocks include single (only one thread executes 24 | * this block), master (only the master thread executes this 25 | * block), atomic (protect a variable by changing it in one step). 26 | */ 27 | #pragma omp critical 28 | std::cout << "Hello World from thread " << thread_id << "!" << std::endl; 29 | 30 | 31 | // Let's wait until all threads reach the following line. 32 | #pragma omp barrier 33 | 34 | /** 35 | * Only one single thread (the first one available) prints this. 36 | * Additionally, "#pragma omp master" would ensure that the actual thread 37 | * executing this block is the one with thread_id equal to 0 (it would be 38 | * equivalent to "if (thread_id == 0)". 39 | */ 40 | #pragma omp single 41 | { 42 | n_threads = omp_get_num_threads(); 43 | std::cout << "Number of threads = " << n_threads << std::endl; 44 | } 45 | } 46 | 47 | // All threads join master thread and terminate. 48 | 49 | return 0; 50 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex02/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | using namespace std::chrono; 8 | using Matrix = Eigen::Matrix; 9 | 10 | int main(int argc, char** argv) { 11 | // parse inputs 12 | if (argc != 2) { 13 | std::cerr << "Usage: " << argv[0] << " matrix size\n"; 14 | std::exit(EXIT_FAILURE); 15 | } 16 | const auto n = atoi(argv[1]); 17 | 18 | // initialize matrices 19 | const Matrix A = Eigen::MatrixXd::Random(n, n); 20 | const Matrix B = Eigen::MatrixXd::Random(n, n); 21 | Matrix C = Eigen::MatrixXd::Zero(n, n); 22 | 23 | // make naive matrix multiplication 24 | const auto t0 = high_resolution_clock::now(); 25 | for (int i = 0; i < n; i++) 26 | for (int k = 0; k < n; k++) 27 | for (int j = 0; j < n; j++) 28 | C.coeffRef(i, j) += A.coeffRef(i, k) * B.coeffRef(k, j); 29 | const auto t1 = high_resolution_clock::now(); 30 | const auto dt = duration_cast(t1 - t0).count(); 31 | std::cout << "Elapsed mm naive: " << dt << " [ms]\n"; 32 | 33 | // check result is correct 34 | const auto t2 = high_resolution_clock::now(); 35 | Matrix Ctrue = A * B; 36 | const auto t3 = high_resolution_clock::now(); 37 | Eigen::MatrixXd err = (Ctrue - C).cwiseAbs(); 38 | std::cout << "Elapsed mm Eigen: " << duration_cast(t3 - t2).count() << " [ms]\n"; 39 | std::cout << "Error: " << err.maxCoeff() << std::endl; 40 | 41 | return 0; 42 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex02/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | using namespace std::chrono; 8 | using Matrix = Eigen::Matrix; 9 | 10 | int main(int argc, char** argv) { 11 | // parse inputs 12 | if (argc != 2) { 13 | std::cerr << "Usage: " << argv[0] << " matrix size\n"; 14 | std::exit(EXIT_FAILURE); 15 | } 16 | const auto n = atoi(argv[1]); 17 | 18 | // initialize matrices 19 | const Matrix A = Eigen::MatrixXd::Random(n, n); 20 | const Matrix B = Eigen::MatrixXd::Random(n, n); 21 | Matrix C = Eigen::MatrixXd::Zero(n, n); 22 | 23 | // make naive matrix multiplication 24 | const auto t0 = high_resolution_clock::now(); 25 | #pragma omp parallel for 26 | for (int i = 0; i < n; i++) 27 | for (int k = 0; k < n; k++) 28 | for (int j = 0; j < n; j++) 29 | C.coeffRef(i, j) += A.coeffRef(i, k) * B.coeffRef(k, j); 30 | const auto t1 = high_resolution_clock::now(); 31 | const auto dt = duration_cast(t1 - t0).count(); 32 | std::cout << "Elapsed mm naive: " << dt << " [ms]\n"; 33 | 34 | // check result is correct 35 | const auto t2 = high_resolution_clock::now(); 36 | Matrix Ctrue = A * B; 37 | const auto t3 = high_resolution_clock::now(); 38 | Eigen::MatrixXd err = (Ctrue - C).cwiseAbs(); 39 | std::cout << "Elapsed mm Eigen: " << duration_cast(t3 - t2).count() << " [ms]\n"; 40 | std::cout << "Error: " << err.maxCoeff() << std::endl; 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex03/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "mpi.h" 11 | #include 12 | 13 | using namespace std::chrono; 14 | 15 | inline double simpson_multithreaded( 16 | std::function const& f, 17 | double a, double b, unsigned int n, 18 | unsigned int num_threads 19 | ) { 20 | double integral = 0.0; 21 | double h = (b - a) / n; 22 | // FILL HERE: add OpenMP directive to parallelize the loop, specify 23 | // - the number of threads `num_threads` to use 24 | // - the shared variables 25 | // - the reduction operation 26 | for (auto i = 0u; i < n; ++i) 27 | { 28 | integral += (f(a + i * h) + 4. * f(a + (i + 0.5) * h) + f(a + (i + 1.) * h)); 29 | } 30 | return (h / 6.) * integral; 31 | } 32 | 33 | 34 | int main(int argc, char** argv) { 35 | // parse arguments 36 | // - the number of intervals in which [0,1] is split into when evaluating the integral 37 | // - the number of OpenMP threads we want to use 38 | if (argc != 3) { 39 | std::cerr << "Usage: " << argv[0] << " number-of-intervals number-of-threads\n"; 40 | std::exit(EXIT_FAILURE); 41 | } 42 | const auto n = atoi(argv[1]); 43 | const auto nthreads = atoi(argv[2]); 44 | 45 | // initialize MPI 46 | MPI_Init(&argc, &argv); 47 | int rank, size; 48 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 49 | MPI_Comm_size(MPI_COMM_WORLD, &size); 50 | 51 | // notice that differently from the other labs we assume that all 52 | // processors know the total number of intervals and that we are 53 | // working in [0, 1] so the broadcast is not needed 54 | // -------------------------------------------------------------- 55 | // number of intervals handled by this proc 56 | const auto n_local /*= FILL HERE*/; 57 | // size of the partition of the interval [0, 1], you can use equal sized partitions 58 | const auto local_interval_size /*= FILL HERE*/; 59 | // start of the partition for this proc 60 | const auto a_local /*= FILL HERE*/; 61 | 62 | // compute the integral and measure elapsed time 63 | const auto t0 = high_resolution_clock::now(); 64 | const double local_integral = simpson_multithreaded( 65 | [](auto x){ return std::sqrt(1 - x*x); }, 66 | a_local, a_local + local_interval_size, n_local, nthreads); 67 | double integral = 0.0; 68 | // use MPI_reduce to compute the value of the integral 69 | // as the sum on the integrals on the partitions of [0, 1] 70 | // FILL HERE 71 | const auto t1 = high_resolution_clock::now(); 72 | const auto dt = duration_cast(t1 - t0).count(); 73 | 74 | if (rank == 0) { 75 | std::cout << std::setprecision(16); 76 | std::cout << "Integral value: " << integral << "\n"; 77 | std::cout << "Error: " << std::abs(integral * 4.0 - M_PI) << "\n"; 78 | std::cout << "Elapsed:" << dt << " [ms]\n"; 79 | } 80 | MPI_Finalize(); 81 | return 0; 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex03/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "mpi.h" 11 | #include 12 | 13 | using namespace std::chrono; 14 | 15 | inline double simpson_multithreaded( 16 | std::function const& f, 17 | double a, double b, unsigned int n, 18 | unsigned int num_threads 19 | ) { 20 | double integral = 0.0; 21 | double h = (b - a) / n; 22 | #pragma omp parallel for num_threads(num_threads) shared(a,h,f,n) reduction(+:integral) 23 | for (auto i = 0u; i < n; ++i) 24 | { 25 | integral += (f(a + i * h) + 4. * f(a + (i + 0.5) * h) + f(a + (i + 1.) * h)); 26 | } 27 | return (h / 6.) * integral; 28 | } 29 | 30 | 31 | int main(int argc, char** argv) { 32 | // parse arguments 33 | // - the number of intervals in which [0,1] is split into when evaluating the integral 34 | // - the number of OpenMP threads we want to use 35 | if (argc != 3) { 36 | std::cerr << "Usage: " << argv[0] << " number-of-intervals number-of-threads\n"; 37 | std::exit(EXIT_FAILURE); 38 | } 39 | const auto n = atoi(argv[1]); 40 | const auto nthreads = atoi(argv[2]); 41 | 42 | // initialize MPI 43 | MPI_Init(&argc, &argv); 44 | int rank, size; 45 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 46 | MPI_Comm_size(MPI_COMM_WORLD, &size); 47 | 48 | // notice that differently from the other labs we assume that all 49 | // processors know the total number of intervals and that we are 50 | // working in [0, 1] so the broadcast is not needed 51 | // -------------------------------------------------------------- 52 | // number of intervals handled by this proc 53 | const auto n_local = n / size + (n % size > rank); 54 | // size of the interval 55 | const auto local_interval_size = 1.0 / size; 56 | // start of the interval 57 | const auto a_local = rank * local_interval_size; 58 | 59 | // compute the integral and measure elapsed time 60 | const auto t0 = high_resolution_clock::now(); 61 | const double local_integral = simpson_multithreaded( 62 | [](auto x){ return std::sqrt(1 - x*x); }, 63 | a_local, a_local + local_interval_size, n_local, nthreads); 64 | double integral = 0.0; 65 | // the value of the integral is the sum on the integrals on the partitions of [0, 1] 66 | MPI_Reduce(&local_integral, &integral, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); 67 | const auto t1 = high_resolution_clock::now(); 68 | const auto dt = duration_cast(t1 - t0).count(); 69 | 70 | if (rank == 0) { 71 | std::cout << std::setprecision(16); 72 | std::cout << "Integral value: " << integral << "\n"; 73 | std::cout << "Error: " << std::abs(integral * 4.0 - M_PI) << "\n"; 74 | std::cout << "Elapsed:" << dt << " [ms]\n"; 75 | } 76 | MPI_Finalize(); 77 | return 0; 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex04/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | constexpr size_t dim = 2; 10 | using Point = std::array; 11 | using KDTree = std::vector>; 12 | 13 | size_t build_kdtree_recursive(const std::vector &points, 14 | std::vector::iterator pts_begin, 15 | std::vector::iterator pts_end, 16 | KDTree &kdtree, 17 | size_t depth) { 18 | if(pts_end - pts_begin == 0) { 19 | return -1; 20 | } 21 | if(pts_end - pts_begin == 1) { 22 | kdtree[*pts_begin] = {-1, -1}; 23 | return *pts_begin; 24 | } 25 | const auto axis = depth % dim; 26 | std::sort(pts_begin, pts_end, 27 | [&](size_t a, size_t b) {return points[a][axis] < points[b][axis];}); 28 | const auto median_idx = (pts_end - pts_begin) / 2; 29 | const auto idx = *(pts_begin + median_idx); 30 | 31 | kdtree[idx].first = build_kdtree_recursive(points, pts_begin, pts_begin + median_idx, kdtree, depth + 1); 32 | kdtree[idx].second = build_kdtree_recursive(points, pts_begin + median_idx + 1, pts_end, kdtree, depth + 1); 33 | 34 | return idx; 35 | } 36 | 37 | KDTree build_kdtree(const std::vector &points) { 38 | KDTree kdtree(points.size()); 39 | std::vector points_idx(points.size()); 40 | std::iota(points_idx.begin(), points_idx.end(), 0); 41 | build_kdtree_recursive(points, points_idx.begin(), points_idx.end(), kdtree, 0); 42 | return kdtree; 43 | } 44 | 45 | int main(int argc, char *argv[]) { 46 | const std::vector points = {{7, 2}, {5, 4}, {9, 6}, {4, 7}, {8, 1}, {2, 3}}; 47 | const auto kdtree = build_kdtree(points); 48 | for(const auto &pp : kdtree) 49 | std::cout << pp.first << " " << pp.second << std::endl; 50 | 51 | return 0; 52 | } -------------------------------------------------------------------------------- /Labs/2022-23/lab06/ex04/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | constexpr size_t dim = 2; 12 | using Point = std::array; 13 | using KDTree = std::vector>; 14 | 15 | size_t build_kdtree_recursive(const std::vector &points, 16 | std::vector::iterator pts_begin, 17 | std::vector::iterator pts_end, 18 | KDTree &kdtree, 19 | size_t depth) { 20 | if(pts_end - pts_begin == 0) { 21 | return -1; 22 | } 23 | if(pts_end - pts_begin == 1) { 24 | kdtree[*pts_begin] = {-1, -1}; 25 | return *pts_begin; 26 | } 27 | const auto axis = depth % dim; 28 | std::sort(pts_begin, pts_end, 29 | [&](size_t a, size_t b) {return points[a][axis] < points[b][axis];}); 30 | const auto median_idx = (pts_end - pts_begin) / 2; 31 | const auto idx = *(pts_begin + median_idx); 32 | 33 | size_t a, b; 34 | auto pointsp = &points; 35 | auto kdtreep = &kdtree; 36 | #pragma omp task shared(a) firstprivate(depth, pointsp, pts_begin, median_idx, kdtreep) 37 | a = build_kdtree_recursive(*pointsp, pts_begin, pts_begin + median_idx, *kdtreep, depth + 1); 38 | #pragma omp task shared(b) firstprivate(depth, pointsp, pts_begin, pts_end, median_idx, kdtreep) 39 | b = build_kdtree_recursive(*pointsp, pts_begin + median_idx + 1, pts_end, *kdtreep, depth + 1); 40 | #pragma omp taskwait 41 | kdtree[idx].first = a; 42 | kdtree[idx].second = b; 43 | 44 | return idx; 45 | } 46 | 47 | KDTree build_kdtree(const std::vector &points) { 48 | KDTree kdtree(points.size()); 49 | std::vector points_idx(points.size()); 50 | std::iota(points_idx.begin(), points_idx.end(), 0); 51 | #pragma omp parallel 52 | { 53 | #pragma omp single 54 | { 55 | build_kdtree_recursive(points, points_idx.begin(), points_idx.end(), kdtree, 0); 56 | } 57 | } 58 | return kdtree; 59 | } 60 | 61 | 62 | int main(int argc, char *argv[]) { 63 | const std::vector points = {{7, 2}, {5, 4}, {9, 6}, {4, 7}, {8, 1}, {2, 3}}; 64 | const auto kdtree = build_kdtree(points); 65 | for(const auto &pp : kdtree) 66 | std::cout << pp.first << " " << pp.second << std::endl; 67 | 68 | return 0; 69 | } -------------------------------------------------------------------------------- /Labs/2023-24/README.md: -------------------------------------------------------------------------------- 1 | # Lab sessions 2 | 3 | ## Schedule: Friday 9:30 - 11:15, room T.0.3 4 | 5 | ## Webex room: [https://politecnicomilano.webex.com/meet/matteo.caldana](https://politecnicomilano.webex.com/meet/matteo.caldana) 6 | 7 | ## Email Teaching assistant: [matteo.caldana@polimi.it](mailto:matteo.caldana@polimi.it) 8 | 9 | ## Email Tutor: [paolojoseph.baioni@polimi.it](mailto:paolojoseph.baioni@polimi.it) 10 | 11 | All the material for the labs is available (only) on [github](https://github.com/HPC-Courses/AMSC-Labs/tree/main/Labs/2023-24) 12 | -------------------------------------------------------------------------------- /Labs/2023-24/bash-intro/an-intro-to-bash.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/bash-intro/an-intro-to-bash.pdf -------------------------------------------------------------------------------- /Labs/2023-24/bash-intro/assets/moving_cli.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/bash-intro/assets/moving_cli.png -------------------------------------------------------------------------------- /Labs/2023-24/bash-intro/assets/shell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/bash-intro/assets/shell.png -------------------------------------------------------------------------------- /Labs/2023-24/bash-intro/assets/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/bash-intro/assets/terminal.png -------------------------------------------------------------------------------- /Labs/2023-24/docker-bugfix-mpi/README.md: -------------------------------------------------------------------------------- 1 | # 1. The MPI `root` problem 2 | When you try to execute a program in parallel in MPI inside the Docker container you will probably encounter an error like the following: 3 | 4 | ![Docker MPI error](./assets/docker-mpi-error.png) 5 | 6 | This is due to the fact that MPI does not want to run as `root` since it has too many privileges. 7 | To solve it we create a user **inside the container** with the two following commands 8 | 9 | ```bash 10 | /usr/sbin/groupadd -r jellyfish && /usr/sbin/useradd -r -g jellyfish jellyfish 11 | chown jellyfish /home/jellyfish && chgrp jellyfish /home/jellyfish && chown jellyfish /root 12 | ``` 13 | 14 | Now exit the container, and, from now on, **every time** you enter the container you should use this command, that makes you enter as the `jellyfish` user 15 | 16 | ```bash 17 | docker exec -u jellyfish -w /home/jellyfish -it hpc-env /bin/bash 18 | ``` 19 | 20 | ### 1.2 A sub-optimal solution 21 | Another way to solve this problem, that we advise against, is to set the two following environmental variables every time you enter the Docker container 22 | ```bash 23 | export OMPI_ALLOW_RUN_AS_ROOT=1 24 | export OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1 25 | ``` 26 | 27 | 28 | # 2. The `Read -1` error 29 | 30 | When running MPI on Docker, you may experience errors like the following: 31 | ``` 32 | Read -1, expected , errno = 1 error 33 | ``` 34 | This is a known issue (see, e.g., [here](https://github.com/open-mpi/ompi/issues/4948)), that can be solved by typing the following command on the terminal: 35 | ```bash 36 | export OMPI_MCA_btl_vader_single_copy_mechanism=none 37 | ``` 38 | You may add this command to your `.bashrc` file, too! 39 | ```bash 40 | printf "\nexport OMPI_MCA_btl_vader_single_copy_mechanism=none\n" >> ~/.bashrc 41 | ``` -------------------------------------------------------------------------------- /Labs/2023-24/docker-bugfix-mpi/assets/docker-mpi-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/docker-bugfix-mpi/assets/docker-mpi-error.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/Docker-F1-pt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/Docker-F1-pt2.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/Docker-F1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/Docker-F1.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/Docker-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/Docker-ext.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/WSL-F1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/WSL-F1.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/WSL-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/WSL-ext.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/config-compiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/config-compiler.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/config-include-std.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/config-include-std.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/eigen-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/eigen-error.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/extensions.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/installation-flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/installation-flowchart.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/lightbulb-edit-include-path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/lightbulb-edit-include-path.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/path-click-pt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/path-click-pt2.png -------------------------------------------------------------------------------- /Labs/2023-24/lab00-setup/assets/path-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab00-setup/assets/path-click.png -------------------------------------------------------------------------------- /Labs/2023-24/lab01-compiling/compiling.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab01-compiling/compiling.pdf -------------------------------------------------------------------------------- /Labs/2023-24/lab01-compiling/doc/compile_steps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab01-compiling/doc/compile_steps.png -------------------------------------------------------------------------------- /Labs/2023-24/lab01-compiling/doc/template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab01-compiling/doc/template.png -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/assets/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab02-stack-and-classes/assets/stack.png -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/assets/stackmemory4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2023-24/lab02-stack-and-classes/assets/stackmemory4.jpg -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex01/hint/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Shape { 9 | public: 10 | // constructor that initializes the name 11 | // pure virtual getter for the area 12 | // getter for the shape name (non virtual) 13 | // virtual destructor 14 | private: 15 | // member with the name of the shape (const) 16 | }; 17 | 18 | // Implement the classes "Circle" and "Rectangle" 19 | // The constructor must be empty, in the sense the name of the shape 20 | // should not be a user's choice 21 | 22 | 23 | int main() { 24 | // Instantiate vector of shapes 25 | // Add some shapes 26 | // Loop over shapes and print 27 | return 0; 28 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex01/solution/main_v1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Shape { 9 | public: 10 | Shape(const std::string& name) : m_name(name) {}; 11 | virtual double getArea() const = 0; 12 | const std::string& getName() const { return m_name; }; 13 | // always make base classes' destructors virtual when they're meant to be manipulated polymorphically. 14 | virtual ~Shape() = default; 15 | private: 16 | const std::string m_name; 17 | }; 18 | 19 | class Circle : public Shape { 20 | public: 21 | Circle(double radius) : Shape("Circle"), m_radius(radius) {}; 22 | virtual double getArea() const override { return m_radius * m_radius * std::numbers::pi_v; }; 23 | virtual ~Circle() override = default; 24 | private: 25 | const double m_radius; 26 | }; 27 | 28 | class Rectangle : public Shape { 29 | public: 30 | Rectangle(double b, double h) : Shape("Rectangle"), m_basis(b), m_height(h) {}; 31 | virtual double getArea() const override { return m_basis * m_height; }; 32 | virtual ~Rectangle() override = default; 33 | private: 34 | const double m_basis, m_height; 35 | }; 36 | 37 | int main() { 38 | std::vector> shapes; 39 | shapes.push_back(std::make_shared(1.0)); 40 | shapes.push_back(std::make_shared(2.5, 0.2)); 41 | 42 | // notice the use of the auto range 43 | for (const auto& s : shapes) { 44 | std::cout << "I am a " << s->getName() << ", my area is: " << s->getArea() << std::endl; 45 | } 46 | return 0; 47 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex01/solution/main_v2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | class Shape { 9 | public: 10 | Shape() = default; 11 | virtual double getArea() const = 0; 12 | // the advantage of this version w.r.t. the other is that we exploit 13 | // static memory, in this way classes consume less memory and the name 14 | // is 'embedded' at compile time into the class. 15 | // Here we *cannot* use std::string since it has dynamic memory 16 | constexpr virtual const char *getName() = 0; 17 | // always make base classes' destructors virtual when they're meant to be manipulated polymorphically. 18 | virtual ~Shape() = default; 19 | }; 20 | 21 | class Circle : public Shape { 22 | public: 23 | Circle(double radius) : Shape(), m_radius(radius) {}; 24 | virtual double getArea() const override { return m_radius * m_radius * std::numbers::pi_v; }; 25 | constexpr virtual const char *getName() override { return "Circle"; }; 26 | virtual ~Circle() override = default; 27 | private: 28 | const double m_radius; 29 | }; 30 | 31 | class Rectangle : public Shape { 32 | public: 33 | Rectangle(double b, double h) : Shape(), m_basis(b), m_height(h) {}; 34 | virtual double getArea() const override { return m_basis * m_height; }; 35 | constexpr virtual const char *getName() override { return "Rectangle"; }; 36 | virtual ~Rectangle() override = default; 37 | private: 38 | const double m_basis, m_height; 39 | }; 40 | 41 | int main() { 42 | std::vector> shapes; 43 | shapes.push_back(std::make_shared(1.0)); 44 | shapes.push_back(std::make_shared(2.5, 0.2)); 45 | 46 | // notice the use of the auto range 47 | for (const auto& s : shapes) { 48 | std::cout << "I am a " << s->getName() << ", my area is: " << s->getArea() << std::endl; 49 | } 50 | return 0; 51 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex02/hint/main.cpp: -------------------------------------------------------------------------------- 1 | #include "newton.hpp" 2 | 3 | int main() { 4 | NewtonSolver solver([](auto x) { return x * x - 2.0; }, [](auto x) { return 2.0 * x; }); 5 | solver.solve(1.0); 6 | 7 | std::cout << "x = " << solver.getResult() << std::endl; 8 | std::cout << "r = " << solver.getResidual() << std::endl; 9 | std::cout << "dx = " << solver.getStep() << std::endl; 10 | std::cout << "iter = " << solver.getIter() << std::endl; 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex02/hint/newton.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NEWTON_H 2 | #define NEWTON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class NewtonSolver { 11 | public: 12 | NewtonSolver( 13 | // function f 14 | // derivative of f 15 | // max iterations 16 | // residual tolerance, by default 2 machine epsilon 17 | // step tolerance, by default 2 machine epsilon 18 | ) 19 | : 20 | /*initialize members*/ { 21 | } 22 | 23 | 24 | // ================================================ 25 | // implement here the public method "solve" that recives as input x_0 26 | // has the following pseudocode 27 | // * reset the history of solutions x_n 28 | // * while not reached max number of iterations 29 | // ** compute residual at current x_iter 30 | // ** if residual too small break 31 | // ** compute derivative at current x_iter 32 | // ** apply newton step 33 | // ** if step is smaller than tolerance break 34 | 35 | // ================================================ 36 | // define here the public "getters" for 37 | // * the result 38 | // * the last step size 39 | // * the history of x_n (return a const reference) 40 | // * the last residual 41 | // * the number of iterations performed 42 | 43 | // ================================================ 44 | // define here the private members, you need 45 | // * the function f 46 | // * the derivative of f 47 | // * max number of iterations 48 | // * the tolerance on the residual 49 | // * the tolerance of the step size 50 | // * the history of the iterations x_n 51 | // * the last evaluation of f 52 | // * the last evaluation of the derivative of f 53 | // * the current residual 54 | // * the current number of iterations 55 | }; 56 | 57 | #endif /* NEWTON_H */ -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex02/solution/main.cpp: -------------------------------------------------------------------------------- 1 | #include "newton.hpp" 2 | 3 | int main() { 4 | NewtonSolver solver([](auto x) { return x * x - 2.0; }, [](auto x) { return 2.0 * x; }); 5 | solver.solve(1.0); 6 | 7 | std::cout << "x = " << solver.getResult() << std::endl; 8 | std::cout << "r = " << solver.getResidual() << std::endl; 9 | std::cout << "dx = " << solver.getStep() << std::endl; 10 | std::cout << "iter = " << solver.getIter() << std::endl; 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex02/solution/newton.cpp: -------------------------------------------------------------------------------- 1 | #include "newton.hpp" 2 | 3 | void NewtonSolver::solve(const double x0) { 4 | m_x.resize(1); 5 | m_x.reserve(m_n_max_it + 1); 6 | m_x[0] = x0; 7 | for (m_iter = 0; m_iter < m_n_max_it; ++m_iter) { 8 | m_res = m_fun(m_x[m_iter]); 9 | 10 | if (std::abs(m_res) < m_tol_fun) 11 | break; 12 | 13 | m_df_dx = m_dfun(m_x[m_iter]); 14 | 15 | m_dx = -m_res / m_df_dx; 16 | m_x.push_back(m_x[m_iter] + m_dx); 17 | 18 | if (std::abs(m_dx) < m_tol_x) 19 | break; 20 | } 21 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab02-stack-and-classes/ex02/solution/newton.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NEWTON_H 2 | #define NEWTON_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class NewtonSolver { 11 | public: 12 | NewtonSolver( 13 | const std::function& fun_, 14 | const std::function& dfun_, 15 | const unsigned int n_max_it_ = 100, 16 | const double tol_fun_ = std::numeric_limits::epsilon(), 17 | const double tol_x_ = std::numeric_limits::epsilon() 18 | ) 19 | : 20 | m_fun(fun_), 21 | m_dfun(dfun_), 22 | m_n_max_it(n_max_it_), 23 | m_tol_fun(tol_fun_), 24 | m_tol_x(tol_x_), 25 | m_x(0), 26 | m_df_dx(0), 27 | m_dx(0), 28 | m_res(0), 29 | m_iter(0) { 30 | } 31 | 32 | void solve(const double x0); 33 | 34 | double getResult() const { 35 | return m_x.back(); 36 | }; 37 | 38 | double getStep() const { 39 | return m_dx; 40 | }; 41 | 42 | const std::vector& getHistory() const { 43 | return m_x; 44 | } 45 | 46 | double getResidual() const { 47 | return m_res; 48 | }; 49 | 50 | unsigned int getIter() const { 51 | return m_iter; 52 | }; 53 | 54 | private: 55 | std::function m_fun; 56 | std::function m_dfun; 57 | 58 | const unsigned int m_n_max_it; 59 | const double m_tol_fun; 60 | const double m_tol_x; 61 | 62 | std::vector m_x; 63 | double m_df_dx; 64 | double m_dx; 65 | double m_res; 66 | unsigned int m_iter; 67 | }; 68 | 69 | #endif /* NEWTON_H */ -------------------------------------------------------------------------------- /Labs/2023-24/lab03-stl-and-templates/ex01/step-1/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | // *use templates to make your matrix usable with different types 18 | // *just like an std::vector can contain different elements, depending on 19 | // *what you specified 20 | template 21 | class SparseMatrix { 22 | public: 23 | // *constructor, takes no parameters but initializes: number of non-zero elements, number of rows and cols to zero 24 | // *getter for number of rows 25 | // *getter for number of cols 26 | // *getter for number of number of non-zero 27 | 28 | // *print function: prints the number of rows, cols and nnz; moreover calls 29 | // _print, the virtual version of print that is specialized in the children class 30 | 31 | 32 | // *abstract virtual method vmult that implements vector multiplication 33 | // *abstract virtual method operator()(size_t i, size_t j) that implements element access in read only (const version) 34 | // *abstract virtual method operator()(size_t i, size_t j) that implements element access in write (returns non-const reference) 35 | // *virtual destructor 36 | 37 | protected: // protected because need to be accessible to children! 38 | // *abstract virtual method _print that prints the matrix 39 | 40 | // *variable to store nnz 41 | // *variable to store number of rows 42 | // *variable to store number of cols 43 | }; 44 | 45 | int main() { 46 | return 0; 47 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab03-stl-and-templates/ex01/step-1/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | class SparseMatrix { 18 | public: 19 | using Vector = std::vector; 20 | SparseMatrix() : m_nnz(0), m_nrows(0), m_ncols(0) {}; 21 | size_t nrows() const { return m_nrows; } 22 | size_t ncols() const { return m_ncols; } 23 | size_t nnz() const { return m_nnz; } 24 | 25 | void print(std::ostream& os = std::cout) const { 26 | os << "nrows: " << m_nrows << " | ncols:" << m_ncols << " | nnz: " << m_nnz << std::endl; 27 | _print(os); 28 | }; 29 | 30 | virtual Vector vmult(const Vector& v) const = 0; 31 | virtual const T& operator()(size_t i, size_t j) const = 0; 32 | virtual T& operator()(size_t i, size_t j) = 0; 33 | virtual ~SparseMatrix() = default; 34 | 35 | protected: 36 | virtual void _print(std::ostream& os) const = 0; 37 | size_t m_nnz; 38 | size_t m_nrows, m_ncols; 39 | }; 40 | 41 | int main() { 42 | return 0; 43 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab03-stl-and-templates/ex01/step-2/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | 17 | template 18 | class SparseMatrix { 19 | public: 20 | using Vector = std::vector; 21 | SparseMatrix() : m_nnz(0), m_nrows(0), m_ncols(0) {}; 22 | size_t nrows() const { return m_nrows; } 23 | size_t ncols() const { return m_ncols; } 24 | size_t nnz() const { return m_nnz; } 25 | 26 | void print(std::ostream& os = std::cout) const { 27 | os << "nrows: " << m_nrows << " | ncols:" << m_ncols << " | nnz: " << m_nnz << std::endl; 28 | _print(os); 29 | }; 30 | 31 | virtual Vector vmult(const Vector& v) const = 0; 32 | virtual const T& operator()(size_t i, size_t j) const = 0; 33 | virtual T& operator()(size_t i, size_t j) = 0; 34 | virtual ~SparseMatrix() = default; 35 | 36 | protected: 37 | virtual void _print(std::ostream& os) const = 0; 38 | size_t m_nnz; 39 | size_t m_nrows, m_ncols; 40 | }; 41 | 42 | template 43 | class MapMatrix : public SparseMatrix { 44 | public: 45 | using Vector = typename SparseMatrix::Vector; 46 | virtual Vector vmult(const Vector& x) const override { 47 | // assert x.size() == m_ncols 48 | // allocate memory for result and initialize to 0 49 | // loop over each element of the matrix and add contribute to result 50 | } 51 | 52 | virtual double& operator()(size_t i, size_t j) override { 53 | // check if we have enough rows, if not add them 54 | // find column entry, if not present add it 55 | // return value reference 56 | } 57 | virtual const double& operator()(size_t i, size_t j) const override { 58 | // return value reference with no check, we use the c++ convention of no bounds check 59 | } 60 | virtual ~MapMatrix() override = default; 61 | protected: 62 | virtual void _print(std::ostream& os) const { 63 | // print the data 64 | } 65 | 66 | std::vector> m_data; 67 | }; 68 | 69 | int main() { 70 | MapMatrix m; 71 | m(0, 0) = 1; 72 | m(1, 1) = 1; 73 | m.print(std::cout); 74 | const auto x = m.vmult({{2, 3}}); 75 | std::cout << x[0] << " " << x[1] << std::endl; 76 | return 0; 77 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab03-stl-and-templates/ex01/step-2/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | class SparseMatrix { 18 | public: 19 | using Vector = std::vector; 20 | SparseMatrix() : m_nnz(0), m_nrows(0), m_ncols(0) {}; 21 | size_t nrows() const { return m_nrows; } 22 | size_t ncols() const { return m_ncols; } 23 | size_t nnz() const { return m_nnz; } 24 | 25 | void print(std::ostream& os = std::cout) const { 26 | os << "nrows: " << m_nrows << " | ncols:" << m_ncols << " | nnz: " << m_nnz << std::endl; 27 | _print(os); 28 | }; 29 | 30 | virtual Vector vmult(const Vector& v) const = 0; 31 | virtual const T& operator()(size_t i, size_t j) const = 0; 32 | virtual T& operator()(size_t i, size_t j) = 0; 33 | virtual ~SparseMatrix() = default; 34 | 35 | protected: 36 | virtual void _print(std::ostream& os) const = 0; 37 | size_t m_nnz; 38 | size_t m_nrows, m_ncols; 39 | }; 40 | 41 | template 42 | class MapMatrix : public SparseMatrix { 43 | public: 44 | using Vector = typename SparseMatrix::Vector; 45 | virtual Vector vmult(const Vector& x) const override { 46 | assert(x.size() == SparseMatrix::m_ncols); 47 | Vector res(x.size()); 48 | for (size_t i = 0; i < m_data.size(); ++i) { 49 | for (const auto& [j, v] : m_data[i]) { 50 | res[i] += x[j] * v; 51 | } 52 | } 53 | return res; 54 | } 55 | 56 | virtual T& operator()(size_t i, size_t j) override { 57 | if (m_data.size() < i + 1) { 58 | m_data.resize(i + 1); 59 | SparseMatrix::m_nrows = i + 1; 60 | } 61 | const auto it = m_data[i].find(j); 62 | if (it == m_data[i].end()) { 63 | SparseMatrix::m_ncols = std::max(SparseMatrix::m_ncols, j + 1); 64 | SparseMatrix::m_nnz++; 65 | return (*m_data[i].emplace(j, 0).first).second; 66 | } 67 | return (*it).second; 68 | } 69 | virtual const T& operator()(size_t i, size_t j) const override { 70 | return m_data[i].at(j); 71 | } 72 | virtual ~MapMatrix() override = default; 73 | protected: 74 | virtual void _print(std::ostream& os) const { 75 | for (size_t i = 0; i < m_data.size(); ++i) { 76 | for (const auto& [j, v] : m_data[i]) 77 | os << i << "," << j << "," << v << std::endl; 78 | } 79 | } 80 | 81 | private: 82 | std::vector> m_data; 83 | }; 84 | 85 | int main() { 86 | MapMatrix m; 87 | m(0, 0) = 1; 88 | m(1, 1) = 1; 89 | std::cout << "Matrix m" << std::endl; 90 | m.print(std::cout); 91 | const auto x = m.vmult({{2, 3}}); 92 | std::cout << "m @ [2, 3]" << std::endl; 93 | std::cout << x[0] << " " << x[1] << std::endl; 94 | return 0; 95 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab03-stl-and-templates/homework/check.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HH_UTILS_HH 2 | #define HH_UTILS_HH 3 | 4 | template 5 | bool eq(const T& lhs, const T& rhs) { 6 | if (lhs.size() != rhs.size()) { 7 | return false; 8 | } 9 | for (auto i = lhs.begin(), j = rhs.begin(); i != lhs.end() && j != rhs.end(); 10 | ++i, ++j) { 11 | if (*i != *j) { 12 | return false; 13 | } 14 | } 15 | return true; 16 | } 17 | 18 | void print_passed(bool b) { 19 | std::cout << (b ? "PASSED" : "FAILED") << std::endl; 20 | } 21 | 22 | void check1(const std::vector &sorted_and_unique) { 23 | print_passed(eq(sorted_and_unique, {{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}})); 24 | } 25 | 26 | void check2(const std::map &word_count) { 27 | print_passed( 28 | eq(word_count, 29 | {{"a", 1}, {"and", 1}, {"at", 2}, {"behind", 1}, 30 | {"different", 1}, {"dust", 1}, {"either", 1}, {"evening", 1}, 31 | {"fear", 1}, {"from", 1}, {"handful", 1}, {"i", 2}, 32 | {"in", 1}, {"meet", 1}, {"morning", 1}, {"of", 1}, 33 | {"or", 1}, {"rising", 1}, {"shadow", 2}, {"show", 2}, 34 | {"something", 1}, {"striding", 1}, {"to", 1}, {"will", 2}, 35 | {"you", 4}, {"your", 2}})); 36 | } 37 | 38 | void check3(const std::vector &fx_at_09) { 39 | print_passed(eq(fx_at_09, {{std::tan(0.9), std::sin(0.9), std::exp(0.9)}})); 40 | } 41 | 42 | void check4(const std::vector &range11even_squares) { 43 | print_passed(eq(range11even_squares, {{0, 4, 16, 36, 64, 100}})); 44 | } 45 | 46 | #endif -------------------------------------------------------------------------------- /Labs/2023-24/lab04-optimization-profiling/ex01/step1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Shape { 10 | public: 11 | Shape() = default; 12 | virtual std::array getNormal() const = 0; 13 | constexpr virtual const char *getName() = 0; 14 | virtual ~Shape() = default; 15 | }; 16 | 17 | class Triangle : public Shape { 18 | public: 19 | Triangle(const std::array, 3> &pts) 20 | : Shape(), m_pts(pts){}; 21 | virtual std::array getNormal() const override; 22 | constexpr virtual const char *getName() override { return "Triangle"; }; 23 | virtual ~Triangle() override = default; 24 | 25 | private: 26 | std::array m_rgb; 27 | std::array, 3> m_pts; 28 | unsigned char m_alpha; 29 | }; 30 | 31 | std::array Triangle::getNormal() const { 32 | // Compute vectors AB and AC 33 | std::array AB, AC, normal; 34 | for (int i = 0; i < 3; ++i) { 35 | AB[i] = m_pts[1][i] - m_pts[0][i]; 36 | AC[i] = m_pts[2][i] - m_pts[0][i]; 37 | } 38 | 39 | // Compute the cross product 40 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 41 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 42 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 43 | 44 | return normal; 45 | }; 46 | 47 | std::array, 3> 48 | get_random_triangle(std::uniform_real_distribution &dist, 49 | std::mt19937 &gen) { 50 | std::array, 3> points; 51 | 52 | // Initialize points with random values 53 | for (int i = 0; i < 3; ++i) { 54 | for (int j = 0; j < 3; ++j) { 55 | points[i][j] = dist(gen); 56 | } 57 | } 58 | return points; 59 | } 60 | 61 | int main(int argc, char **argv) { 62 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 63 | std::vector> shapes; 64 | 65 | using namespace std::chrono; 66 | 67 | std::random_device rd; 68 | std::mt19937 gen(rd()); 69 | std::uniform_real_distribution dist(-10.0, 10.0); 70 | 71 | { 72 | const auto t0 = high_resolution_clock::now(); 73 | for (size_t i = 0; i < N; ++i) { 74 | shapes.push_back( 75 | std::make_shared(get_random_triangle(dist, gen))); 76 | } 77 | const auto t1 = high_resolution_clock::now(); 78 | const auto dt = duration_cast(t1 - t0).count(); 79 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 80 | } 81 | 82 | std::vector> normals; 83 | { 84 | const auto t0 = high_resolution_clock::now(); 85 | 86 | normals.reserve(shapes.size()); 87 | for (size_t i = 0; i < N; ++i) { 88 | normals.push_back(shapes[i]->getNormal()); 89 | } 90 | const auto t1 = high_resolution_clock::now(); 91 | const auto dt = duration_cast(t1 - t0).count(); 92 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 93 | << std::endl; 94 | } 95 | 96 | return 0; 97 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab04-optimization-profiling/ex01/step2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Triangle { 10 | public: 11 | Triangle(const std::array, 3> &pts) : m_pts(pts){}; 12 | std::array getNormal() const { 13 | // Compute vectors AB and AC 14 | std::array AB, AC, normal; 15 | for (int i = 0; i < 3; ++i) { 16 | AB[i] = m_pts[1][i] - m_pts[0][i]; 17 | AC[i] = m_pts[2][i] - m_pts[0][i]; 18 | } 19 | 20 | // Compute the cross product 21 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 22 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 23 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 24 | 25 | return normal; 26 | }; 27 | constexpr const char *getName() { return "Triangle"; }; 28 | 29 | private: 30 | std::array m_rgb; 31 | std::array, 3> m_pts; 32 | unsigned char m_alpha; 33 | }; 34 | 35 | std::array, 3> 36 | get_random_triangle(std::uniform_real_distribution &dist, 37 | std::mt19937 &gen) { 38 | std::array, 3> points; 39 | 40 | // Initialize points with random values 41 | for (int i = 0; i < 3; ++i) { 42 | for (int j = 0; j < 3; ++j) { 43 | points[i][j] = dist(gen); 44 | } 45 | } 46 | return points; 47 | } 48 | 49 | int main(int argc, char **argv) { 50 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 51 | std::vector shapes; 52 | 53 | using namespace std::chrono; 54 | 55 | std::random_device rd; 56 | std::mt19937 gen(rd()); 57 | std::uniform_real_distribution dist(-10.0, 10.0); 58 | 59 | { 60 | const auto t0 = high_resolution_clock::now(); 61 | for (size_t i = 0; i < N; ++i) { 62 | shapes.emplace_back(get_random_triangle(dist, gen)); 63 | } 64 | const auto t1 = high_resolution_clock::now(); 65 | const auto dt = duration_cast(t1 - t0).count(); 66 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 67 | } 68 | 69 | std::vector> normals; 70 | { 71 | const auto t0 = high_resolution_clock::now(); 72 | 73 | normals.reserve(shapes.size()); 74 | for (size_t i = 0; i < N; ++i) { 75 | normals.push_back(shapes[i].getNormal()); 76 | } 77 | const auto t1 = high_resolution_clock::now(); 78 | const auto dt = duration_cast(t1 - t0).count(); 79 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 80 | << std::endl; 81 | } 82 | 83 | return 0; 84 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab04-optimization-profiling/ex01/step3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Triangle { 10 | public: 11 | Triangle(const std::array, 3> &pts) : m_pts(pts){}; 12 | std::array getNormal() const { 13 | // Compute vectors AB and AC 14 | std::array AB, AC, normal; 15 | for (int i = 0; i < 3; ++i) { 16 | AB[i] = m_pts[1][i] - m_pts[0][i]; 17 | AC[i] = m_pts[2][i] - m_pts[0][i]; 18 | } 19 | 20 | // Compute the cross product 21 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 22 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 23 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 24 | 25 | return normal; 26 | }; 27 | constexpr const char *getName() { return "Triangle"; }; 28 | 29 | private: 30 | std::array m_rgba; 31 | std::array, 3> m_pts; 32 | }; 33 | 34 | std::array, 3> 35 | get_random_triangle(std::uniform_real_distribution &dist, 36 | std::mt19937 &gen) { 37 | std::array, 3> points; 38 | 39 | // Initialize points with random values 40 | for (int i = 0; i < 3; ++i) { 41 | for (int j = 0; j < 3; ++j) { 42 | points[i][j] = dist(gen); 43 | } 44 | } 45 | return points; 46 | } 47 | 48 | int main(int argc, char **argv) { 49 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 50 | std::vector shapes; 51 | 52 | using namespace std::chrono; 53 | 54 | std::random_device rd; 55 | std::mt19937 gen(rd()); 56 | std::uniform_real_distribution dist(-10.0, 10.0); 57 | 58 | { 59 | const auto t0 = high_resolution_clock::now(); 60 | for (size_t i = 0; i < N; ++i) { 61 | shapes.emplace_back(get_random_triangle(dist, gen)); 62 | } 63 | const auto t1 = high_resolution_clock::now(); 64 | const auto dt = duration_cast(t1 - t0).count(); 65 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 66 | } 67 | 68 | std::vector> normals; 69 | { 70 | const auto t0 = high_resolution_clock::now(); 71 | 72 | normals.reserve(shapes.size()); 73 | for (size_t i = 0; i < N; ++i) { 74 | normals.push_back(shapes[i].getNormal()); 75 | } 76 | const auto t1 = high_resolution_clock::now(); 77 | const auto dt = duration_cast(t1 - t0).count(); 78 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 79 | << std::endl; 80 | } 81 | 82 | return 0; 83 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab04-optimization-profiling/ex01/step4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Triangle { 10 | public: 11 | Triangle(const std::array, 3> &pts) : m_pts(pts){}; 12 | std::array getNormal() const { 13 | // Compute vectors AB and AC 14 | std::array AB, AC, normal; 15 | for (int i = 0; i < 3; ++i) { 16 | AB[i] = m_pts[1][i] - m_pts[0][i]; 17 | AC[i] = m_pts[2][i] - m_pts[0][i]; 18 | } 19 | 20 | // Compute the cross product 21 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 22 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 23 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 24 | 25 | return normal; 26 | }; 27 | constexpr const char *getName() { return "Triangle"; }; 28 | 29 | private: 30 | std::array m_rgba; 31 | std::array, 3> m_pts; 32 | }; 33 | 34 | std::array, 3> 35 | get_random_triangle(std::uniform_real_distribution &dist, 36 | std::mt19937 &gen) { 37 | std::array, 3> points; 38 | 39 | // Initialize points with random values 40 | for (int i = 0; i < 3; ++i) { 41 | for (int j = 0; j < 3; ++j) { 42 | points[i][j] = dist(gen); 43 | } 44 | } 45 | return points; 46 | } 47 | 48 | int main(int argc, char **argv) { 49 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 50 | std::vector shapes; 51 | 52 | using namespace std::chrono; 53 | 54 | std::random_device rd; 55 | std::mt19937 gen(rd()); 56 | std::uniform_real_distribution dist(-10.0, 10.0); 57 | 58 | { 59 | const auto t0 = high_resolution_clock::now(); 60 | shapes.reserve(N); 61 | for (size_t i = 0; i < N; ++i) { 62 | shapes.emplace_back(get_random_triangle(dist, gen)); 63 | } 64 | const auto t1 = high_resolution_clock::now(); 65 | const auto dt = duration_cast(t1 - t0).count(); 66 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 67 | } 68 | 69 | std::vector> normals; 70 | { 71 | const auto t0 = high_resolution_clock::now(); 72 | 73 | normals.reserve(shapes.size()); 74 | for (size_t i = 0; i < N; ++i) { 75 | normals.push_back(shapes[i].getNormal()); 76 | } 77 | const auto t1 = high_resolution_clock::now(); 78 | const auto dt = duration_cast(t1 - t0).count(); 79 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 80 | << std::endl; 81 | } 82 | 83 | return 0; 84 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab04-optimization-profiling/homework/integer-list.cpp: -------------------------------------------------------------------------------- 1 | // Build and print a list of integers. 2 | #include "integer-list.hpp" 3 | 4 | int main(int argc, char **argv) { 5 | int c = 1; 6 | 7 | // create a new list 8 | Node start(c); 9 | 10 | // add 10 nodes to the list 11 | while (c < 10) { 12 | ++c; 13 | start.appendNew(c); 14 | } 15 | 16 | // print the list 17 | start.print(); 18 | 19 | // find the node with value 5 20 | Node *t = start.find(5); 21 | 22 | // erase this node 23 | t->erase(); 24 | 25 | // print the list again 26 | start.print(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /Labs/2023-24/lab04-optimization-profiling/homework/integer-list.hpp: -------------------------------------------------------------------------------- 1 | // A linked-list of integers 2 | #include 3 | 4 | class Node { 5 | public: 6 | // constructors / destructor 7 | Node() : next(nullptr), previous(nullptr) {} 8 | 9 | Node(int a) : next(nullptr), previous(nullptr), data(a) {} 10 | 11 | ~Node() { 12 | if (next) 13 | delete next; 14 | } 15 | 16 | // set/get interface 17 | void setData(int a) { data = a; } 18 | 19 | int getData() { return data; } 20 | 21 | void setNext(Node *theNext) { next = theNext; } 22 | 23 | Node *getNext() { return next; } 24 | 25 | void setPrevious(Node *thePrevious) { previous = thePrevious; } 26 | 27 | Node *getPrevious() { return previous; } 28 | 29 | // list capabilities 30 | // return true if node is the first of the list, false otherwise 31 | bool isFirst() { return !previous; } 32 | 33 | // return true if node is the last of the list, false otherwise 34 | bool isLast() { return !next; } 35 | 36 | // return the size of the sublist starting from this node 37 | int size() { 38 | Node *t = this; 39 | int ret = 1; 40 | 41 | while (!t->isLast()) { 42 | t = next; 43 | ++ret; 44 | } 45 | 46 | return ret; 47 | } 48 | 49 | // append a new given node at the end of the list 50 | void append(Node *theNext) { 51 | Node *t = this; 52 | while (!t->isLast()) 53 | t = next; 54 | 55 | t->setNext(theNext); 56 | theNext->setPrevious(t); 57 | } 58 | 59 | // create a new node with value 'a' and append it at the end of the 60 | // list 61 | void appendNew(int a) { 62 | Node *t = this; 63 | while (!t->isLast()) 64 | t = next; 65 | 66 | Node *theNewNode = new Node(a); 67 | t->setNext(theNewNode); 68 | theNewNode->setPrevious(t); 69 | } 70 | 71 | // remove this node from the list 72 | void erase() { 73 | previous->setNext(next); 74 | next->setPrevious(previous); 75 | } 76 | 77 | // replace this node with a given node 78 | void replaceWith(Node *replacement) { 79 | previous->setNext(replacement); 80 | next->setPrevious(replacement); 81 | } 82 | 83 | // find first node with a specified value in sublist starting from 84 | // this node return nullptr if not found 85 | Node *find(int value) { 86 | if (data == value) 87 | return this; 88 | 89 | Node *t = this; 90 | while (!t->isLast()) { 91 | t = next; 92 | if (t->getData() == value) 93 | return t; 94 | } 95 | 96 | return nullptr; 97 | } 98 | 99 | // print the data in the sublist starting from this node 100 | void print() { 101 | Node *t = this; 102 | while (!t->isLast()) { 103 | std::cout << t.getData() << ", "; 104 | t = next; 105 | } 106 | std::cout << t.getData() << std::endl; 107 | } 108 | 109 | protected: 110 | // pointer to next node in list 111 | Node *next; 112 | 113 | // pointer to previous node in list 114 | Node *previous; 115 | 116 | private: 117 | // the integer data stored in the node 118 | int data; 119 | }; 120 | -------------------------------------------------------------------------------- /Labs/2023-24/lab05-mpi/ex01/helloworld.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | // Initialize the MPI environment 7 | MPI_Init(&argc, &argv); 8 | 9 | // Get the number of processes and their rank 10 | int world_size, world_rank; 11 | MPI_Comm_size(MPI_COMM_WORLD, &world_size); 12 | MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); 13 | 14 | // Get the name of the processor 15 | char processor_name[MPI_MAX_PROCESSOR_NAME]; 16 | int name_len; 17 | MPI_Get_processor_name(processor_name, &name_len); 18 | 19 | // Print off a hello world message 20 | std::cout << "Hello world from processor " << processor_name << ", rank " 21 | << world_rank << " out of " << world_size << " processors\n"; 22 | 23 | // Finalize the MPI environment. 24 | MPI_Finalize(); 25 | return 0; 26 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab05-mpi/ex01/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | MPI_Init(&argc, &argv); 9 | int rank, size; 10 | MPI_Comm_size(MPI_COMM_WORLD, &size); 11 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 12 | 13 | const std::string greeting = 14 | /*more than one argument*/ ? /*second string passed with argv*/ 15 | : /*default greeting*/; 16 | const std::string message = /*assemble the message*/; 17 | 18 | if (rank) { 19 | // send your local message size and then message to processor 0 20 | } else { // you are process 0 21 | // output your message 22 | // receive all the messages from the other processors and output them 23 | } 24 | 25 | MPI_Finalize(); 26 | return 0; 27 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab05-mpi/ex01/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | MPI_Init(&argc, &argv); 9 | int rank, size; 10 | MPI_Comm_size(MPI_COMM_WORLD, &size); 11 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 12 | 13 | const std::string greeting = (argc > 1) ? argv[1] : "world"; 14 | const std::string message = "Hello " + greeting + ", from rank " + 15 | std::to_string(rank) + " of " + 16 | std::to_string(size); 17 | 18 | if (rank > 0) { 19 | const unsigned length = unsigned(message.size()); 20 | MPI_Send(&length, 1, MPI_UNSIGNED, 0, 0, MPI_COMM_WORLD); 21 | MPI_Send(&message[0], length, MPI_CHAR, 0, 1, MPI_COMM_WORLD); 22 | } else { 23 | std::cout << message << std::endl; 24 | std::string recv_message; 25 | unsigned recv_length; 26 | for (int r = 1; r < size; r++) { 27 | MPI_Recv(&recv_length, 1, MPI_UNSIGNED, r, 0, MPI_COMM_WORLD, 28 | MPI_STATUS_IGNORE); 29 | recv_message.resize(recv_length); 30 | MPI_Recv(&recv_message[0], recv_length, MPI_CHAR, r, 1, MPI_COMM_WORLD, 31 | MPI_STATUS_IGNORE); 32 | std::cout << recv_message << std::endl; 33 | } 34 | } 35 | 36 | MPI_Finalize(); 37 | return 0; 38 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab05-mpi/ex02/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | std::array compute_gradient( 12 | const std::function &)> &f, 13 | const std::array &x, double h) { 14 | // initialize gradient 15 | // FILL HERE 16 | 17 | // get rank and size 18 | // FILL HERE 19 | 20 | // subdivide computations by processor 21 | for (/*FILL HERE*/) { 22 | auto x_plus_h(x), x_minus_h(x); 23 | x_plus_h[i] += h; 24 | x_minus_h[i] -= h; 25 | gradient[i] = (f(x_plus_h) - f(x_minus_h)) / 2.0 / h; 26 | } 27 | 28 | // send result to main proc 29 | for (size_t i = 0; i < N; ++i) { // for each component of the gradient 30 | const int belongs_to_proc = 31 | /*FILL HERE: compute to which proc it belongs to*/; 32 | if (belongs_to_proc /*!= 0*/) { // then we need to send it to proc 0 33 | if (/*FILL HERE: belong to us*/) { 34 | // FILL HERE: we send it to proc 0 35 | } else if (/*FILL HERE: we are proc 0*/) { 36 | // FILL HERE: we receive the data 37 | } 38 | } 39 | } 40 | return gradient; 41 | } 42 | 43 | int main(int argc, char *argv[]) { 44 | // init MPI, get rank and size 45 | // FILL HERE 46 | 47 | // define types 48 | using Vector = std::array; 49 | using Function = std::function; 50 | // define the field f of which we compute the gradient at the point where we 51 | // evaluate it 52 | const Function f = [](auto v) { 53 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 54 | 2 * v[3] * v[3] * v[3]; 55 | }; 56 | const Vector y = {0, 1, std::numbers::pi, 2}; 57 | // define the correct value of the gradient 58 | const Vector g_true = {1, std::exp(1), 0, 24}; 59 | // compute the gradient with our function 60 | const Vector g = compute_gradient(f, y, 1e-8); 61 | 62 | // check if the solution is correct 63 | if (/*FILL HERE*/) { 64 | std::cout << std::setprecision(4) << std::scientific; 65 | for (size_t i = 0; i < g.size(); ++i) { 66 | const auto err = std::abs(g[i] - g_true[i]); 67 | std::cout << i << " | " << g[i] << (err < 1e-6 ? " PASS" : " FAIL") 68 | << std::endl; 69 | } 70 | } 71 | // FILL HERE: finalize 72 | return 0; 73 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab05-mpi/ex02/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | std::array compute_gradient( 10 | const std::function &)> &f, 11 | const std::array &x, double h) { 12 | std::array gradient; 13 | 14 | for (size_t i = 0; i < N; i++) { 15 | auto x_plus_h(x), x_minus_h(x); 16 | x_plus_h[i] += h; 17 | x_minus_h[i] -= h; 18 | gradient[i] = (f(x_plus_h) - f(x_minus_h)) / 2.0 / h; 19 | } 20 | return gradient; 21 | } 22 | 23 | int main() { 24 | // define types 25 | using Vector = std::array; 26 | using Function = std::function; 27 | 28 | // define the field f of which we compute the gradient at the point where we 29 | // evaluate it 30 | const Function f = [](auto v) { 31 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 32 | 2 * v[3] * v[3] * v[3]; 33 | }; 34 | const Vector y = {0, 1, std::numbers::pi, 2}; 35 | // define the correct value of the gradient 36 | const Vector g_true = {1, std::exp(1), 0, 24}; 37 | // compute the gradient with our function 38 | const Vector g = compute_gradient(f, y, 1e-8); 39 | 40 | // check if the result is correct 41 | std::cout << std::setprecision(4) << std::scientific; 42 | for (size_t i = 0; i < g.size(); ++i) { 43 | const auto err = std::abs(g[i] - g_true[i]); 44 | std::cout << i << " | " << g[i] << (err < 1e-6 ? " PASS" : " FAIL") 45 | << std::endl; 46 | } 47 | 48 | return 0; 49 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab05-mpi/ex02/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | std::array compute_gradient( 12 | const std::function &)> &f, 13 | const std::array &x, double h) { 14 | // initialize gradient 15 | std::array gradient; 16 | 17 | // get rank and size 18 | int rank, size; 19 | MPI_Comm_size(MPI_COMM_WORLD, &size); 20 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 21 | 22 | // subdivide computations by processor 23 | for (size_t i = rank; i < N; i += size) { 24 | auto x_plus_h(x), x_minus_h(x); 25 | x_plus_h[i] += h; 26 | x_minus_h[i] -= h; 27 | gradient[i] = (f(x_plus_h) - f(x_minus_h)) / 2.0 / h; 28 | } 29 | 30 | // send result to main proc 31 | for (size_t i = 0; i < N; ++i) { 32 | const int belongs_to_proc = i % size; 33 | const int tag = i / size; 34 | if (belongs_to_proc /*!= 0*/) { 35 | if (rank == belongs_to_proc) { 36 | MPI_Send(&gradient[i], 1, MPI_DOUBLE, 0, tag, MPI_COMM_WORLD); 37 | } else if (rank == 0) { 38 | MPI_Recv(&gradient[i], 1, MPI_DOUBLE, belongs_to_proc, tag, 39 | MPI_COMM_WORLD, MPI_STATUS_IGNORE); 40 | } 41 | } 42 | } 43 | return gradient; 44 | } 45 | 46 | int main(int argc, char *argv[]) { 47 | // init MPI 48 | MPI_Init(&argc, &argv); 49 | int rank, size; 50 | MPI_Comm_size(MPI_COMM_WORLD, &size); 51 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 52 | 53 | // define types 54 | using Vector = std::array; 55 | using Function = std::function; 56 | // define the field f of which we compute the gradient at the point where we 57 | // evaluate it 58 | const Function f = [](auto v) { 59 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 60 | 2 * v[3] * v[3] * v[3]; 61 | }; 62 | const Vector y = {0, 1, std::numbers::pi, 2}; 63 | // define the correct value of the gradient 64 | const Vector g_true = {1, std::exp(1), 0, 24}; 65 | // compute the gradient with our function 66 | const Vector g = compute_gradient(f, y, 1e-8); 67 | 68 | // check if the solution is correct 69 | if (rank == 0) { 70 | std::cout << std::setprecision(4) << std::scientific; 71 | for (size_t i = 0; i < g.size(); ++i) { 72 | const auto err = std::abs(g[i] - g_true[i]); 73 | std::cout << i << " | " << g[i] << (err < 1e-6 ? " PASS" : " FAIL") 74 | << std::endl; 75 | } 76 | } 77 | MPI_Finalize(); 78 | return 0; 79 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab06-openmp/ex02/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(int argc, char** argv) { 4 | int id = 0; 5 | std::cout << "Hello World from thread " << id << std::endl; 6 | 7 | return 0; 8 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab06-openmp/ex02/solution_v1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | #pragma omp parallel 7 | { 8 | int thread_id = omp_get_thread_num(); 9 | std::cout << "Hello World from thread " << thread_id << "!" << std::endl; 10 | } 11 | 12 | return 0; 13 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab06-openmp/ex02/solution_v2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int argc, char **argv) { 6 | int thread_id; 7 | int n_threads; 8 | 9 | /** 10 | * Fork a team of threads with each thread 11 | * having a private thread_id variable. 12 | */ 13 | #pragma omp parallel private(thread_id) shared(n_threads) 14 | { 15 | thread_id = omp_get_thread_num(); 16 | 17 | /** 18 | * The following block is "critical", i.e. we only let one thread 19 | * execute this block at a time. 20 | * 21 | * Other types of blocks include single (only one thread executes 22 | * this block), master (only the master thread executes this 23 | * block), atomic (protect a variable by changing it in one step). 24 | */ 25 | #pragma omp critical 26 | std::cout << "Hello World from thread " << thread_id << "!" << std::endl; 27 | 28 | // Let's wait until all threads reach the following line. 29 | #pragma omp barrier 30 | 31 | /** 32 | * Only one single thread (the first one available) prints this. 33 | * Additionally, "#pragma omp master" would ensure that the actual thread 34 | * executing this block is the one with thread_id equal to 0 (it would be 35 | * equivalent to "if (thread_id == 0)". 36 | */ 37 | #pragma omp single 38 | { 39 | n_threads = omp_get_num_threads(); 40 | std::cout << "Number of threads = " << n_threads << std::endl; 41 | } 42 | } 43 | 44 | // All threads join master thread and terminate. 45 | 46 | return 0; 47 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab06-openmp/ex03/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::chrono; 8 | using Matrix = Eigen::Matrix; 9 | 10 | int main(int argc, char** argv) { 11 | // parse inputs 12 | if (argc != 2) { 13 | std::cerr << "Usage: " << argv[0] << " matrix size\n"; 14 | std::exit(EXIT_FAILURE); 15 | } 16 | const auto n = atoi(argv[1]); 17 | 18 | // initialize matrices 19 | const Matrix A = Eigen::MatrixXd::Random(n, n); 20 | const Matrix B = Eigen::MatrixXd::Random(n, n); 21 | Matrix C = Eigen::MatrixXd::Zero(n, n); 22 | 23 | // make naive matrix multiplication 24 | const auto t0 = high_resolution_clock::now(); 25 | for (int i = 0; i < n; i++) 26 | for (int j = 0; j < n; j++) 27 | for (int k = 0; k < n; k++) 28 | C.coeffRef(i, j) += A.coeffRef(i, k) * B.coeffRef(k, j); 29 | const auto t1 = high_resolution_clock::now(); 30 | const auto dt = duration_cast(t1 - t0).count(); 31 | std::cout << "Elapsed mm naive: " << dt << " [ms]\n"; 32 | 33 | // check result is correct 34 | const auto t2 = high_resolution_clock::now(); 35 | Matrix Ctrue = A * B; 36 | const auto t3 = high_resolution_clock::now(); 37 | Eigen::MatrixXd err = (Ctrue - C).cwiseAbs(); 38 | std::cout << "Elapsed mm Eigen: " 39 | << duration_cast(t3 - t2).count() << " [ms]\n"; 40 | std::cout << "Error: " << err.maxCoeff() << std::endl; 41 | 42 | return 0; 43 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab06-openmp/ex03/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace std::chrono; 8 | using Matrix = Eigen::Matrix; 9 | 10 | int main(int argc, char** argv) { 11 | // parse inputs 12 | if (argc != 3) { 13 | std::cerr << "Usage: " << argv[0] << " num-threads matrix-size \n"; 14 | std::exit(EXIT_FAILURE); 15 | } 16 | const auto num_threads = std::atoi(argv[1]); 17 | const auto n = std::atoi(argv[2]); 18 | 19 | // initialize matrices 20 | const Matrix A = Eigen::MatrixXd::Random(n, n); 21 | const Matrix B = Eigen::MatrixXd::Random(n, n); 22 | Matrix C = Eigen::MatrixXd::Zero(n, n); 23 | 24 | omp_set_num_threads(num_threads); 25 | std::cout << "Number of threads: " << num_threads << std::endl; 26 | 27 | // make naive matrix multiplication 28 | const auto t0 = high_resolution_clock::now(); 29 | 30 | #pragma omp parallel for 31 | for (int i = 0; i < n; i++) 32 | for (int j = 0; j < n; j++) 33 | for (int k = 0; k < n; k++) 34 | C.coeffRef(i, j) += A.coeffRef(i, k) * B.coeffRef(k, j); 35 | const auto t1 = high_resolution_clock::now(); 36 | const auto dt = duration_cast(t1 - t0).count(); 37 | std::cout << "Elapsed mm naive: " << dt << " [ms]\n"; 38 | 39 | // check result is correct 40 | const auto t2 = high_resolution_clock::now(); 41 | Matrix Ctrue = A * B; 42 | const auto t3 = high_resolution_clock::now(); 43 | Eigen::MatrixXd err = (Ctrue - C).cwiseAbs(); 44 | std::cout << "Elapsed mm Eigen: " 45 | << duration_cast(t3 - t2).count() << " [ms]\n"; 46 | std::cout << "Error: " << err.maxCoeff() << std::endl; 47 | 48 | return 0; 49 | } -------------------------------------------------------------------------------- /Labs/2023-24/lab06-openmp/ex04/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace std::chrono; 14 | 15 | inline double simpson_multithreaded(std::function const& f, 16 | double a, double b, unsigned int n, 17 | unsigned int num_threads) { 18 | double integral = 0.0; 19 | double h = (b - a) / n; 20 | // FILL HERE: add OpenMP directive to parallelize the loop, specify 21 | // - the number of threads `num_threads` to use 22 | // - the shared variables 23 | // - the reduction operation 24 | for (auto i = 0u; i < n; ++i) { 25 | integral += 26 | (f(a + i * h) + 4. * f(a + (i + 0.5) * h) + f(a + (i + 1.) * h)); 27 | } 28 | return (h / 6.) * integral; 29 | } 30 | 31 | int main(int argc, char** argv) { 32 | // parse arguments 33 | // - the number of intervals in which [0,1] is split into when evaluating the 34 | // integral 35 | // - the number of OpenMP threads we want to use 36 | if (argc != 3) { 37 | std::cerr << "Usage: " << argv[0] 38 | << " number-of-intervals number-of-threads\n"; 39 | std::exit(EXIT_FAILURE); 40 | } 41 | const auto n = atoi(argv[1]); 42 | const auto nthreads = atoi(argv[2]); 43 | 44 | // initialize MPI 45 | MPI_Init(&argc, &argv); 46 | int rank, size; 47 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 48 | MPI_Comm_size(MPI_COMM_WORLD, &size); 49 | 50 | // notice that differently from the other labs we assume that all 51 | // processors know the total number of intervals and that we are 52 | // working in [0, 1] so the broadcast is not needed 53 | // -------------------------------------------------------------- 54 | // number of intervals handled by this proc 55 | const auto n_local /*= FILL HERE*/; 56 | // size of the partition of the interval [0, 1], you can use equal sized 57 | // partitions 58 | const auto local_interval_size /*= FILL HERE*/; 59 | // start of the partition for this proc 60 | const auto a_local /*= FILL HERE*/; 61 | 62 | // compute the integral and measure elapsed time 63 | const auto t0 = high_resolution_clock::now(); 64 | const double local_integral = simpson_multithreaded( 65 | [](auto x) { return std::sqrt(1 - x * x); }, a_local, 66 | a_local + local_interval_size, n_local, nthreads); 67 | double integral = 0.0; 68 | // use MPI_reduce to compute the value of the integral 69 | // as the sum on the integrals on the partitions of [0, 1] 70 | // FILL HERE 71 | const auto t1 = high_resolution_clock::now(); 72 | const auto dt = duration_cast(t1 - t0).count(); 73 | 74 | if (rank == 0) { 75 | std::cout << std::setprecision(16); 76 | std::cout << "Integral value: " << integral << "\n"; 77 | std::cout << "Error: " << std::abs(integral * 4.0 - M_PI) << "\n"; 78 | std::cout << "Elapsed:" << dt << " [ms]\n"; 79 | } 80 | MPI_Finalize(); 81 | return 0; 82 | } 83 | -------------------------------------------------------------------------------- /Labs/2024-25/README.md: -------------------------------------------------------------------------------- 1 | Labs for aY 14-15 2 | -------------------------------------------------------------------------------- /Labs/2024-25/exercise_ball/problem_statement.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/exercise_ball/problem_statement.pdf -------------------------------------------------------------------------------- /Labs/2024-25/exercise_ball/src/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | OPENMPFLAG = #-fopenmp 3 | OPENMPLINKINGFLAG = 4 | LIBS = #-L/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/lib -llis 5 | CXXFLAGS = -std=c++20 -O2 -fpic #-g 6 | CPPFLAGS = -I../include #-I/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/include #-DNDEBUG 7 | PROGRAM = main 8 | OBJ = main.o utils.o 9 | 10 | 11 | 12 | .PHONY : all clean distclean 13 | 14 | all : main 15 | 16 | 17 | main : $(OBJ) 18 | $(CXX) $(OPENMPLINKINGFLAG) $(LIBS) $(OBJ) $(OPENMPFLAG) -o main.exe 19 | 20 | 21 | $(OBJ) : %.o : %.cpp 22 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(OPENMPFLAG) -c $< 23 | 24 | 25 | 26 | clean : 27 | $(RM) *.o 28 | 29 | distclean : clean 30 | $(RM) main.exe 31 | -------------------------------------------------------------------------------- /Labs/2024-25/exercise_ball/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "utils.h" 6 | 7 | int main(int argc, char** argv) 8 | { 9 | 10 | static const int max_iter = 1e3; 11 | static const double rtol = 1e-5; 12 | static const double stol = 1e-5; 13 | static const double tolerance_reaction_term = 1e-7; 14 | 15 | static const double step_size = 1e-6; 16 | static const double v_0 = 10; 17 | static const double theta_0 = 0; 18 | static const double gravity = 9.81; 19 | static const double R = 30; 20 | double dt = 1e-3; 21 | 22 | if (std::abs(v_0)>=std::sqrt(gravity*R)) 23 | { 24 | std::cout << "The initial condition is such that the ball already detaches at the beginning of the simulation" << std::endl; 25 | exit(1); 26 | } 27 | 28 | 29 | double vtheta_n = v_0; 30 | double theta_n = theta_0; 31 | double current_reaction; 32 | 33 | type_fun func = [&theta_n, &vtheta_n, &dt](double const& vtheta_np1){return (-vtheta_np1 + vtheta_n + gravity*dt*sin(theta_n + dt/R*vtheta_np1));}; 34 | 35 | type_fun2 vincular_reaction = [&theta_n, &vtheta_n](){return (gravity*cos(theta_n) - vtheta_n*vtheta_n/R);}; 36 | 37 | 38 | Newton root_method(vtheta_n, func, max_iter, rtol, stol, nullopt, step_size); 39 | 40 | double time = 0.; 41 | while (true) 42 | { 43 | vtheta_n = root_method.solve(); 44 | theta_n += dt/R*vtheta_n; 45 | 46 | current_reaction = vincular_reaction(); 47 | time += dt; // update time 48 | 49 | cout << "Angle at which the ball detaches: " << theta_n*180/M_PI << ", Current reaction term value: " << current_reaction << ", Time at which the ball detaches: " << time << endl; 50 | 51 | if (std::abs(current_reaction) 3 | #include 4 | #include 5 | 6 | int 7 | Root_finder::getIter() {return iter;} 8 | 9 | double 10 | Root_finder::getRes() {return residual;} 11 | 12 | vector 13 | Root_finder::getXs() {return xs;} 14 | 15 | Root_finder::Root_finder(type_fun func, int max_iter, double rtol, double stol) : function_root(func), max_iter(max_iter), rtol(rtol), stol(stol) 16 | { 17 | // allocate here the size!, it's important 18 | xs.reserve(max_iter); 19 | } 20 | 21 | Newton::Newton(const double initial_guess, type_fun func, int max_iter, double rtol, double stol, optional der_func_in, double step_size) : initial_guess(initial_guess), Root_finder(func, max_iter, rtol, stol), step_size(step_size) 22 | { 23 | xs.push_back(initial_guess); 24 | der_fun = der_func_in ? *der_func_in : [this](double const& x){return ((this->function_root(x+this->step_size)-this->function_root(x))/this->step_size);}; 25 | is_Newton = der_func_in ? true : false; 26 | } 27 | 28 | Bisection::Bisection(double a, double b, type_fun func, int max_iter, double rtol, double stol) : a(a), b(b), Root_finder(func, max_iter, rtol, stol) {} 29 | 30 | -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/Docker-F1-pt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/Docker-F1-pt2.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/Docker-F1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/Docker-F1.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/Docker-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/Docker-ext.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/WSL-F1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/WSL-F1.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/WSL-ext.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/WSL-ext.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/config-compiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/config-compiler.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/config-include-std.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/config-include-std.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/eigen-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/eigen-error.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/extensions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/extensions.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/installation-flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/installation-flowchart.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/lightbulb-edit-include-path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/lightbulb-edit-include-path.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/path-click-pt2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/path-click-pt2.png -------------------------------------------------------------------------------- /Labs/2024-25/lab00-setup/assets/path-click.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab00-setup/assets/path-click.png -------------------------------------------------------------------------------- /Labs/2024-25/lab01-compiling/examples/REAME.txt: -------------------------------------------------------------------------------- 1 | in the src folder, if you already have Eigen in the compiler path, simply type 2 | ```bash 3 | make 4 | ``` 5 | if you want a faster building, type 6 | ```bash 7 | make -j 8 | ``` 9 | To see if Eigen is in the -I path, check the env variable CPLUS_INCLUDE_PATH, 10 | if it is not in the path, add it by typing 11 | ```bash 12 | export CPLUS_INCLUDE_PATH="$CPLUS_INCLUDE_PATH:/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/eigen/3.3.9/include/eigen3" 13 | ``` 14 | 15 | To rm the *.o files, 16 | ```bash 17 | make clean 18 | ``` 19 | 20 | the program once built needs two input two execute, provide two number basically, e.g., 21 | ```bash 22 | ./main.exe 3 2 23 | ``` -------------------------------------------------------------------------------- /Labs/2024-25/lab01-compiling/examples/include/exe.h: -------------------------------------------------------------------------------- 1 | #ifndef EXE_HH 2 | #define EXE_HH 3 | 4 | #include 5 | std::string whos_lower(const double& a, const double& b); 6 | 7 | #endif 8 | -------------------------------------------------------------------------------- /Labs/2024-25/lab01-compiling/examples/src/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | OPENMPFLAG = #-fopenmp 3 | OPENMPLINKINGFLAG = 4 | LIBS = #-Linclude/lis-2.0.32/BUILD/lib -llis 5 | CXXFLAGS = -std=c++20 -O2 -fpic #-g 6 | CPPFLAGS = -I../include #-DNDEBUG 7 | PROGRAM = main 8 | OBJ = main.o exe.o 9 | 10 | 11 | 12 | .PHONY : all clean distclean 13 | 14 | all : main 15 | 16 | 17 | main : $(OBJ) 18 | $(CXX) $(OPENMPLINKINGFLAG) $(LIBS) $(OBJ) $(OPENMPFLAG) -o main.exe 19 | 20 | 21 | $(OBJ) : %.o : %.cpp 22 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(OPENMPFLAG) -c $< 23 | 24 | 25 | 26 | clean : 27 | $(RM) *.o 28 | 29 | distclean : clean 30 | $(RM) main.exe -------------------------------------------------------------------------------- /Labs/2024-25/lab01-compiling/examples/src/exe.cpp: -------------------------------------------------------------------------------- 1 | #include "exe.h" 2 | #include 3 | #include 4 | 5 | std::string whos_lower(const double& a, const double& b) { 6 | return a 2 | #include 3 | #include "exe.h" 4 | 5 | #ifndef M_PII 6 | #define M_PII 2 7 | #endif 8 | 9 | int main(int argc, char** argv) 10 | { 11 | if (argc != 3) 12 | { 13 | throw std::runtime_error( 14 | (std::ostringstream() 15 | << "please provide just two inputs '" 16 | ).str() 17 | ); 18 | } 19 | #ifdef M_PI 20 | std::cout << "here inside" << std::endl; 21 | #endif 22 | 23 | std::cout << "Successfully included Eigen. " << M_PI << " " << M_PII << " " << whos_lower(*argv[1], *argv[2]) << std::endl; 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/assets/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab02-classes/assets/stack.png -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/assets/stackmemory4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab02-classes/assets/stackmemory4.jpg -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_1/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab02-classes/exe_1/.DS_Store -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_1/include/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab02-classes/exe_1/include/.DS_Store -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_1/include/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_HH 2 | #define UTILS_HH 3 | 4 | 5 | #include 6 | #include 7 | using namespace std; 8 | 9 | #ifndef M_PI 10 | #define M_PI 4*std::atan(1.) 11 | #endif 12 | 13 | 14 | // in the .h file you declare the functions, classes, ..., while in the .cpp you define them 15 | 16 | class Shape{ 17 | public: 18 | Shape() = default; 19 | virtual ~Shape() = default; //Always make virtual the destructor when working with class inheritance 20 | virtual double getArea() = 0; //Pure virtual class 21 | constexpr virtual const char *getName() = 0; //Make also this virtual to resolve at compile time the name 22 | }; 23 | 24 | class Circle: public Shape{ 25 | private: 26 | const double radius; 27 | 28 | public: 29 | Circle(const double); 30 | virtual ~Circle() override = default; 31 | double getArea() override { return(M_PI*radius*radius); }; 32 | constexpr virtual const char *getName() override { return "Circle"; }; 33 | }; 34 | 35 | class Rectangle: public Shape{ 36 | private: 37 | const double height; 38 | const double basis; 39 | 40 | public: 41 | Rectangle(const double, const double); 42 | virtual ~Rectangle() override = default; 43 | double getArea() override { return(height*basis); }; 44 | constexpr virtual const char *getName() override { return "Rectangle"; }; 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_1/src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/lab02-classes/exe_1/src/.DS_Store -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_1/src/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | OPENMPFLAG = #-fopenmp 3 | OPENMPLINKINGFLAG = 4 | LIBS = -L/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/lib -llis 5 | CXXFLAGS = -std=c++20 -O2 -fpic #-g 6 | CPPFLAGS = -I../include -I/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/include #-DNDEBUG 7 | PROGRAM = main 8 | OBJ = main.o utils.o 9 | 10 | 11 | 12 | .PHONY : all clean distclean 13 | 14 | all : main 15 | 16 | 17 | main : $(OBJ) 18 | $(CXX) $(OPENMPLINKINGFLAG) $(LIBS) $(OBJ) $(OPENMPFLAG) -o main.exe 19 | 20 | 21 | $(OBJ) : %.o : %.cpp 22 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(OPENMPFLAG) -c $< 23 | 24 | 25 | 26 | clean : 27 | $(RM) *.o 28 | 29 | distclean : clean 30 | $(RM) main.exe 31 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_1/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | 6 | int main(int argc, char** argv) 7 | { 8 | // The stack is usually restricted to a limited amount of memory per program/process, 9 | // so allocating very large objects in local scope with atomatic storage is a bad idea, 10 | // that could lead to stack overflow when the program runs out of stack memory. 11 | // This is why if the object to be allocated is large, it is better to allocate it on the heap memory, 12 | // to do so, use shared_ptr or unique_ptr 13 | vector > shapes; 14 | shapes.push_back(make_shared(2.0)); 15 | shapes.push_back(make_shared(3.1, 1)); 16 | 17 | // The problem when dealing with polymorphism is that the vector allocated above with shared_ptr does not store 18 | // the elements in a continuous memory location, this means that it is preferable to avoid to build a big vector 19 | // few elements are enough. 20 | // Polymorphism is useful in case we have, e.g., multiple quadrature methods that are all of course quadrature rules, so they share something in common. 21 | // In general, you will have few quadrature rule implemented in a program and can be implemented with the help of the isomorphism. 22 | // In general, always have a look at the performances. Data locality when accessing elements is important because CPU likes to access elements in that way. 23 | 24 | 25 | for (const auto& s : shapes) 26 | { 27 | cout << s->getArea() << " " << s->getName() << endl; 28 | } 29 | 30 | return 0; 31 | } 32 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_1/src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | #include 4 | 5 | 6 | 7 | Circle::Circle(const double radius) : radius(radius) 8 | {} 9 | 10 | Rectangle::Rectangle(const double height, const double basis) : height(height), basis(basis) 11 | {} 12 | 13 | 14 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_2/src/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | OPENMPFLAG = #-fopenmp 3 | OPENMPLINKINGFLAG = 4 | LIBS = #-L/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/lib -llis 5 | CXXFLAGS = -std=c++20 -O2 -fpic #-g 6 | CPPFLAGS = -I../include #-I/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/include #-DNDEBUG 7 | PROGRAM = main 8 | OBJ = main.o utils.o 9 | 10 | 11 | 12 | .PHONY : all clean distclean 13 | 14 | all : main 15 | 16 | 17 | main : $(OBJ) 18 | $(CXX) $(OPENMPLINKINGFLAG) $(LIBS) $(OBJ) $(OPENMPFLAG) -o main.exe 19 | 20 | 21 | $(OBJ) : %.o : %.cpp 22 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(OPENMPFLAG) -c $< 23 | 24 | 25 | 26 | clean : 27 | $(RM) *.o 28 | 29 | distclean : clean 30 | $(RM) main.exe 31 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_2/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "utils.h" 5 | 6 | static const double step_size = 1e-6; 7 | static type_fun func = [](double const& x){return x*x-2.;}; 8 | static type_fun der_func = [](double const& x){return 2.*x;}; 9 | 10 | int main(int argc, char** argv) 11 | { 12 | 13 | vector > root_method; 14 | root_method.reserve(3); 15 | 16 | int max_iter = 1e3; 17 | double rtol = 1e-5; 18 | double stol = 1e-5; 19 | 20 | root_method.push_back(make_shared(2.0, func, max_iter, rtol, stol, nullopt, step_size)); // Secant method 21 | root_method.push_back(make_shared(2.0, func, max_iter, rtol, stol, der_func)); // Pure Newton method 22 | root_method.push_back(make_shared(0, 2, func, max_iter, rtol, stol)); // Bisection 23 | 24 | for (const auto& r : root_method) 25 | { 26 | cout << r->solve() << " " << r->getName() << " " << r->getIter() << endl; 27 | } 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/exe_2/src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | #include 3 | #include 4 | 5 | int 6 | Root_finder::getIter() {return iter;} 7 | 8 | double 9 | Root_finder::getRes() {return residual;} 10 | 11 | vector 12 | Root_finder::getXs() {return xs;} 13 | 14 | Root_finder::Root_finder(type_fun func, int max_iter, double rtol, double stol) : function_root(func), max_iter(max_iter), rtol(rtol), stol(stol) 15 | { 16 | // allocate here the size!, it's important 17 | xs.reserve(max_iter); 18 | } 19 | 20 | Newton::Newton(const double initial_guess, type_fun func, int max_iter, double rtol, double stol, optional der_func_in, double step_size) : initial_guess(initial_guess), Root_finder(func, max_iter, rtol, stol), step_size(step_size) 21 | { 22 | xs.push_back(initial_guess); 23 | der_fun = der_func_in ? *der_func_in : [this](double const& x){return ((this->function_root(x+this->step_size)-this->function_root(x))/this->step_size);}; 24 | is_Newton = der_func_in ? true : false; 25 | } 26 | 27 | Bisection::Bisection(double a, double b, type_fun func, int max_iter, double rtol, double stol) : a(a), b(b), Root_finder(func, max_iter, rtol, stol) {} 28 | 29 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/memory/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | OPENMPFLAG = #-fopenmp 3 | OPENMPLINKINGFLAG = 4 | LIBS = #-L/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/lib -llis 5 | CXXFLAGS = -std=c++20 -O2 -fpic #-g 6 | CPPFLAGS = #-I../include -I/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/include #-DNDEBUG 7 | PROGRAM = memory 8 | OBJ = memory.o #exe.o 9 | 10 | 11 | 12 | .PHONY : all clean distclean 13 | 14 | all : main 15 | 16 | 17 | main : $(OBJ) 18 | $(CXX) $(OPENMPLINKINGFLAG) $(LIBS) $(OBJ) $(OPENMPFLAG) -o main.exe 19 | 20 | 21 | $(OBJ) : %.o : %.cpp 22 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(OPENMPFLAG) -c $< 23 | 24 | 25 | 26 | clean : 27 | $(RM) *.o 28 | 29 | distclean : clean 30 | $(RM) main.exe 31 | -------------------------------------------------------------------------------- /Labs/2024-25/lab02-classes/memory/memory.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct Counter { 6 | Counter() { ++count; } 7 | Counter(const Counter&) { ++count; } 8 | ~Counter() { --count; } 9 | static size_t count; 10 | }; 11 | 12 | size_t Counter::count = 0; 13 | 14 | void a_function() { 15 | int z; 16 | std::vector v(3); 17 | std::cout << "Heap memory 2 (v after init): " << v.data() << std::endl; 18 | std::cout << "Stack memory (z): " << &z << std::endl; 19 | } 20 | 21 | int main() { 22 | std::cout << "Static memory: " << &Counter::count << std::endl; 23 | int y; 24 | int a[3]; 25 | Counter c; 26 | 27 | std::cout << "Stack memory (y): " << &y << std::endl; 28 | std::cout << "Stack memory (a): " << &a << std::endl; 29 | std::cout << "Stack memory (c): " << &c << std::endl; 30 | a_function(); 31 | 32 | std::vector v(3); 33 | auto pt = std::make_shared(); 34 | std::cout << "Heap memory (v after init): " << v.data() << std::endl; 35 | std::cout << "Heap memory (pt): " << pt.get() << std::endl; 36 | for(int i = 0; i < 1e5; i++) 37 | v.push_back(0); 38 | std::cout << "Heap memory (v after push_back): " << v.data() << std::endl; 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /Labs/2024-25/lab03-stl-and-templates/exe/src/Makefile: -------------------------------------------------------------------------------- 1 | CXX = g++ 2 | OPENMPFLAG = #-fopenmp 3 | OPENMPLINKINGFLAG = 4 | LIBS = #-L/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/lib -llis 5 | CXXFLAGS = -std=c++20 -O2 -fpic #-g 6 | CPPFLAGS = -I../include #-I/u/sw/toolchains/gcc-glibc/11.2.0/pkgs/lis/2.0.30/include #-DNDEBUG 7 | PROGRAM = main 8 | OBJ = main.o #utils.o 9 | 10 | 11 | 12 | .PHONY : all clean distclean 13 | 14 | all : main 15 | 16 | 17 | main : $(OBJ) 18 | $(CXX) $(OPENMPLINKINGFLAG) $(LIBS) $(OBJ) $(OPENMPFLAG) -o main.exe 19 | 20 | 21 | $(OBJ) : %.o : %.cpp 22 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(OPENMPFLAG) -c $< 23 | 24 | 25 | 26 | clean : 27 | $(RM) *.o 28 | 29 | distclean : clean 30 | $(RM) main.exe 31 | -------------------------------------------------------------------------------- /Labs/2024-25/lab03-stl-and-templates/exe/src/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | struct event_counter 17 | { 18 | double cumtime; 19 | int count; 20 | }; 21 | 22 | static clock_t c_start, c_diff; 23 | static double c_msec; 24 | static std::map timing_report; 25 | 26 | #define tic() c_start = clock (); 27 | 28 | #define toc(X) \ 29 | c_diff = clock () - c_start; \ 30 | c_msec = (double)c_diff * 1000 / (double)CLOCKS_PER_SEC; \ 31 | timing_report[X].cumtime += c_msec; \ 32 | timing_report[X].count++; \ 33 | std::cout << X << std::endl << "Elapsed time : " << c_msec \ 34 | << "ms" << std::endl; 35 | 36 | 37 | // *use templates to make your matrix usable with different types 38 | // *just like an std::vector can contain different elements, depending on 39 | // *what you specified 40 | template 41 | class SparseMatrix { 42 | public: 43 | using Vector = std::vector; 44 | SparseMatrix() {}; 45 | size_t nrows() const { return m_nrows; } 46 | size_t ncols() const { return m_ncols; } 47 | size_t nnz() const { return m_nnz; } 48 | 49 | void print(std::ostream& os = std::cout) const { 50 | os << "nrows: " << m_nrows << " | ncols:" << m_ncols << " | nnz: " << m_nnz << std::endl; 51 | }; 52 | 53 | 54 | virtual Vector vmult(const Vector& v) const = 0; 55 | virtual const T& operator()(size_t i, size_t j) const = 0; 56 | virtual T& operator()(size_t i, size_t j) = 0; 57 | virtual ~SparseMatrix() = default; 58 | 59 | protected: 60 | size_t m_nnz = 0; 61 | size_t m_nrows = 0, m_ncols = 0; 62 | }; 63 | 64 | int main() { 65 | tic(); 66 | //SparseMatrix A; 67 | //A.print(); 68 | toc("first part!!"); 69 | 70 | 71 | 72 | return 0; 73 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab04-optimization-profiling/ex01/step1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Shape { 10 | public: 11 | Shape() = default; 12 | virtual std::array getNormal() const = 0; 13 | constexpr virtual const char *getName() = 0; 14 | virtual ~Shape() = default; 15 | }; 16 | 17 | class Triangle : public Shape { 18 | public: 19 | Triangle(const std::array, 3> &pts) 20 | : Shape(), m_pts(pts){}; 21 | virtual std::array getNormal() const override; 22 | constexpr virtual const char *getName() override { return "Triangle"; }; 23 | virtual ~Triangle() override = default; 24 | 25 | private: 26 | std::array m_rgb; 27 | std::array, 3> m_pts; 28 | unsigned char m_alpha; 29 | }; 30 | 31 | std::array Triangle::getNormal() const { 32 | // Compute vectors AB and AC 33 | std::array AB, AC, normal; 34 | for (int i = 0; i < 3; ++i) { 35 | AB[i] = m_pts[1][i] - m_pts[0][i]; 36 | AC[i] = m_pts[2][i] - m_pts[0][i]; 37 | } 38 | 39 | // Compute the cross product 40 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 41 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 42 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 43 | 44 | return normal; 45 | }; 46 | 47 | std::array, 3> 48 | get_random_triangle(std::uniform_real_distribution &dist, 49 | std::mt19937 &gen) { 50 | std::array, 3> points; 51 | 52 | // Initialize points with random values 53 | for (int i = 0; i < 3; ++i) { 54 | for (int j = 0; j < 3; ++j) { 55 | points[i][j] = dist(gen); 56 | } 57 | } 58 | return points; 59 | } 60 | 61 | int main(int argc, char **argv) { 62 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 63 | std::vector> shapes; 64 | 65 | using namespace std::chrono; 66 | 67 | std::random_device rd; 68 | std::mt19937 gen(rd()); 69 | std::uniform_real_distribution dist(-10.0, 10.0); 70 | 71 | { 72 | const auto t0 = high_resolution_clock::now(); 73 | for (size_t i = 0; i < N; ++i) { 74 | shapes.push_back( 75 | std::make_shared(get_random_triangle(dist, gen))); 76 | } 77 | const auto t1 = high_resolution_clock::now(); 78 | const auto dt = duration_cast(t1 - t0).count(); 79 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 80 | } 81 | 82 | std::vector> normals; 83 | { 84 | const auto t0 = high_resolution_clock::now(); 85 | 86 | normals.reserve(shapes.size()); 87 | for (size_t i = 0; i < N; ++i) { 88 | normals.push_back(shapes[i]->getNormal()); 89 | } 90 | const auto t1 = high_resolution_clock::now(); 91 | const auto dt = duration_cast(t1 - t0).count(); 92 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 93 | << std::endl; 94 | } 95 | 96 | return 0; 97 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab04-optimization-profiling/ex01/step2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Triangle { 10 | public: 11 | Triangle(const std::array, 3> &pts) : m_pts(pts){}; 12 | std::array getNormal() const { 13 | // Compute vectors AB and AC 14 | std::array AB, AC, normal; 15 | for (int i = 0; i < 3; ++i) { 16 | AB[i] = m_pts[1][i] - m_pts[0][i]; 17 | AC[i] = m_pts[2][i] - m_pts[0][i]; 18 | } 19 | 20 | // Compute the cross product 21 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 22 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 23 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 24 | 25 | return normal; 26 | }; 27 | constexpr const char *getName() { return "Triangle"; }; 28 | 29 | private: 30 | std::array m_rgb; 31 | std::array, 3> m_pts; 32 | unsigned char m_alpha; 33 | }; 34 | 35 | std::array, 3> 36 | get_random_triangle(std::uniform_real_distribution &dist, 37 | std::mt19937 &gen) { 38 | std::array, 3> points; 39 | 40 | // Initialize points with random values 41 | for (int i = 0; i < 3; ++i) { 42 | for (int j = 0; j < 3; ++j) { 43 | points[i][j] = dist(gen); 44 | } 45 | } 46 | return points; 47 | } 48 | 49 | int main(int argc, char **argv) { 50 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 51 | std::vector shapes; 52 | 53 | using namespace std::chrono; 54 | 55 | std::random_device rd; 56 | std::mt19937 gen(rd()); 57 | std::uniform_real_distribution dist(-10.0, 10.0); 58 | 59 | { 60 | const auto t0 = high_resolution_clock::now(); 61 | for (size_t i = 0; i < N; ++i) { 62 | shapes.emplace_back(get_random_triangle(dist, gen)); 63 | } 64 | const auto t1 = high_resolution_clock::now(); 65 | const auto dt = duration_cast(t1 - t0).count(); 66 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 67 | } 68 | 69 | std::vector> normals; 70 | { 71 | const auto t0 = high_resolution_clock::now(); 72 | 73 | normals.reserve(shapes.size()); 74 | for (size_t i = 0; i < N; ++i) { 75 | normals.push_back(shapes[i].getNormal()); 76 | } 77 | const auto t1 = high_resolution_clock::now(); 78 | const auto dt = duration_cast(t1 - t0).count(); 79 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 80 | << std::endl; 81 | } 82 | 83 | return 0; 84 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab04-optimization-profiling/ex01/step3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Triangle { 10 | public: 11 | Triangle(const std::array, 3> &pts) : m_pts(pts){}; 12 | std::array getNormal() const { 13 | // Compute vectors AB and AC 14 | std::array AB, AC, normal; 15 | for (int i = 0; i < 3; ++i) { 16 | AB[i] = m_pts[1][i] - m_pts[0][i]; 17 | AC[i] = m_pts[2][i] - m_pts[0][i]; 18 | } 19 | 20 | // Compute the cross product 21 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 22 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 23 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 24 | 25 | return normal; 26 | }; 27 | constexpr const char *getName() { return "Triangle"; }; 28 | 29 | private: 30 | std::array m_rgba; 31 | std::array, 3> m_pts; 32 | }; 33 | 34 | std::array, 3> 35 | get_random_triangle(std::uniform_real_distribution &dist, 36 | std::mt19937 &gen) { 37 | std::array, 3> points; 38 | 39 | // Initialize points with random values 40 | for (int i = 0; i < 3; ++i) { 41 | for (int j = 0; j < 3; ++j) { 42 | points[i][j] = dist(gen); 43 | } 44 | } 45 | return points; 46 | } 47 | 48 | int main(int argc, char **argv) { 49 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 50 | std::vector shapes; 51 | 52 | using namespace std::chrono; 53 | 54 | std::random_device rd; 55 | std::mt19937 gen(rd()); 56 | std::uniform_real_distribution dist(-10.0, 10.0); 57 | 58 | { 59 | const auto t0 = high_resolution_clock::now(); 60 | for (size_t i = 0; i < N; ++i) { 61 | shapes.emplace_back(get_random_triangle(dist, gen)); 62 | } 63 | const auto t1 = high_resolution_clock::now(); 64 | const auto dt = duration_cast(t1 - t0).count(); 65 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 66 | } 67 | 68 | std::vector> normals; 69 | { 70 | const auto t0 = high_resolution_clock::now(); 71 | 72 | normals.reserve(shapes.size()); 73 | for (size_t i = 0; i < N; ++i) { 74 | normals.push_back(shapes[i].getNormal()); 75 | } 76 | const auto t1 = high_resolution_clock::now(); 77 | const auto dt = duration_cast(t1 - t0).count(); 78 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 79 | << std::endl; 80 | } 81 | 82 | return 0; 83 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab04-optimization-profiling/ex01/step4.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class Triangle { 10 | public: 11 | Triangle(const std::array, 3> &pts) : m_pts(pts){}; 12 | std::array getNormal() const { 13 | // Compute vectors AB and AC 14 | std::array AB, AC, normal; 15 | for (int i = 0; i < 3; ++i) { 16 | AB[i] = m_pts[1][i] - m_pts[0][i]; 17 | AC[i] = m_pts[2][i] - m_pts[0][i]; 18 | } 19 | 20 | // Compute the cross product 21 | normal[0] = AB[1] * AC[2] - AB[2] * AC[1]; 22 | normal[1] = AB[2] * AC[0] - AB[0] * AC[2]; 23 | normal[2] = AB[0] * AC[1] - AB[1] * AC[0]; 24 | 25 | return normal; 26 | }; 27 | constexpr const char *getName() { return "Triangle"; }; 28 | 29 | private: 30 | std::array m_rgba; 31 | std::array, 3> m_pts; 32 | }; 33 | 34 | std::array, 3> 35 | get_random_triangle(std::uniform_real_distribution &dist, 36 | std::mt19937 &gen) { 37 | std::array, 3> points; 38 | 39 | // Initialize points with random values 40 | for (int i = 0; i < 3; ++i) { 41 | for (int j = 0; j < 3; ++j) { 42 | points[i][j] = dist(gen); 43 | } 44 | } 45 | return points; 46 | } 47 | 48 | int main(int argc, char **argv) { 49 | const auto N = argc > 1 ? std::atoll(argv[1]) : 1000000ull; 50 | std::vector shapes; 51 | 52 | using namespace std::chrono; 53 | 54 | std::random_device rd; 55 | std::mt19937 gen(rd()); 56 | std::uniform_real_distribution dist(-10.0, 10.0); 57 | 58 | { 59 | const auto t0 = high_resolution_clock::now(); 60 | shapes.reserve(N); 61 | for (size_t i = 0; i < N; ++i) { 62 | shapes.emplace_back(get_random_triangle(dist, gen)); 63 | } 64 | const auto t1 = high_resolution_clock::now(); 65 | const auto dt = duration_cast(t1 - t0).count(); 66 | std::cout << "Elapsed for initialization: " << dt << " [ms]" << std::endl; 67 | } 68 | 69 | std::vector> normals; 70 | { 71 | const auto t0 = high_resolution_clock::now(); 72 | 73 | normals.reserve(shapes.size()); 74 | for (size_t i = 0; i < N; ++i) { 75 | normals.push_back(shapes[i].getNormal()); 76 | } 77 | const auto t1 = high_resolution_clock::now(); 78 | const auto dt = duration_cast(t1 - t0).count(); 79 | std::cout << "Elapsed for computation of norm: " << dt << " [ms]" 80 | << std::endl; 81 | } 82 | 83 | return 0; 84 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab04-optimization-profiling/homework/integer-list.cpp: -------------------------------------------------------------------------------- 1 | // Build and print a list of integers. 2 | #include "integer-list.hpp" 3 | 4 | int main(int argc, char **argv) { 5 | int c = 1; 6 | 7 | // create a new list 8 | Node start(c); 9 | 10 | // add 10 nodes to the list 11 | while (c < 10) { 12 | ++c; 13 | start.appendNew(c); 14 | } 15 | 16 | // print the list 17 | start.print(); 18 | 19 | // find the node with value 5 20 | Node *t = start.find(5); 21 | 22 | // erase this node 23 | t->erase(); 24 | 25 | // print the list again 26 | start.print(); 27 | 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /Labs/2024-25/lab04-optimization-profiling/homework/integer-list.hpp: -------------------------------------------------------------------------------- 1 | // A linked-list of integers 2 | #include 3 | 4 | class Node { 5 | public: 6 | // constructors / destructor 7 | Node() : next(nullptr), previous(nullptr) {} 8 | 9 | Node(int a) : next(nullptr), previous(nullptr), data(a) {} 10 | 11 | ~Node() { 12 | if (next) 13 | delete next; 14 | } 15 | 16 | // set/get interface 17 | void setData(int a) { data = a; } 18 | 19 | int getData() { return data; } 20 | 21 | void setNext(Node *theNext) { next = theNext; } 22 | 23 | Node *getNext() { return next; } 24 | 25 | void setPrevious(Node *thePrevious) { previous = thePrevious; } 26 | 27 | Node *getPrevious() { return previous; } 28 | 29 | // list capabilities 30 | // return true if node is the first of the list, false otherwise 31 | bool isFirst() { return !previous; } 32 | 33 | // return true if node is the last of the list, false otherwise 34 | bool isLast() { return !next; } 35 | 36 | // return the size of the sublist starting from this node 37 | int size() { 38 | Node *t = this; 39 | int ret = 1; 40 | 41 | while (!t->isLast()) { 42 | t = next; 43 | ++ret; 44 | } 45 | 46 | return ret; 47 | } 48 | 49 | // append a new given node at the end of the list 50 | void append(Node *theNext) { 51 | Node *t = this; 52 | while (!t->isLast()) 53 | t = next; 54 | 55 | t->setNext(theNext); 56 | theNext->setPrevious(t); 57 | } 58 | 59 | // create a new node with value 'a' and append it at the end of the 60 | // list 61 | void appendNew(int a) { 62 | Node *t = this; 63 | while (!t->isLast()) 64 | t = next; 65 | 66 | Node *theNewNode = new Node(a); 67 | t->setNext(theNewNode); 68 | theNewNode->setPrevious(t); 69 | } 70 | 71 | // remove this node from the list 72 | void erase() { 73 | previous->setNext(next); 74 | next->setPrevious(previous); 75 | } 76 | 77 | // replace this node with a given node 78 | void replaceWith(Node *replacement) { 79 | previous->setNext(replacement); 80 | next->setPrevious(replacement); 81 | } 82 | 83 | // find first node with a specified value in sublist starting from 84 | // this node return nullptr if not found 85 | Node *find(int value) { 86 | if (data == value) 87 | return this; 88 | 89 | Node *t = this; 90 | while (!t->isLast()) { 91 | t = next; 92 | if (t->getData() == value) 93 | return t; 94 | } 95 | 96 | return nullptr; 97 | } 98 | 99 | // print the data in the sublist starting from this node 100 | void print() { 101 | Node *t = this; 102 | while (!t->isLast()) { 103 | std::cout << t.getData() << ", "; 104 | t = next; 105 | } 106 | std::cout << t.getData() << std::endl; 107 | } 108 | 109 | protected: 110 | // pointer to next node in list 111 | Node *next; 112 | 113 | // pointer to previous node in list 114 | Node *previous; 115 | 116 | private: 117 | // the integer data stored in the node 118 | int data; 119 | }; 120 | -------------------------------------------------------------------------------- /Labs/2024-25/lab05-mpi/ex01/helloworld.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int main(int argc, char* argv[]) { 6 | // Initialize the MPI environment 7 | MPI_Init(&argc, &argv); 8 | 9 | // Get the number of processes and their rank 10 | int world_size, world_rank; 11 | MPI_Comm_size(MPI_COMM_WORLD, &world_size); 12 | MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); 13 | 14 | // Get the name of the processor 15 | char processor_name[MPI_MAX_PROCESSOR_NAME]; 16 | int name_len; 17 | MPI_Get_processor_name(processor_name, &name_len); 18 | 19 | // Print off a hello world message 20 | std::cout << "Hello world from processor " << processor_name << ", rank " 21 | << world_rank << " out of " << world_size << " processors\n"; 22 | 23 | // Finalize the MPI environment. 24 | MPI_Finalize(); 25 | return 0; 26 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab05-mpi/ex01/hint.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | MPI_Init(&argc, &argv); 9 | int rank, size; 10 | MPI_Comm_size(MPI_COMM_WORLD, &size); 11 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 12 | 13 | const std::string greeting = 14 | /*more than one argument*/ ? /*second string passed with argv*/ 15 | : /*default greeting*/; 16 | const std::string message = /*assemble the message*/; 17 | 18 | if (rank) { 19 | // send your local message size and then message to processor 0 20 | } else { // you are process 0 21 | // output your message 22 | // receive all the messages from the other processors and output them 23 | } 24 | 25 | MPI_Finalize(); 26 | return 0; 27 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab05-mpi/ex01/sol.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | int main(int argc, char* argv[]) { 9 | MPI_Init(&argc, &argv); 10 | MPI_Status status; 11 | int rank, size; 12 | MPI_Comm_size(MPI_COMM_WORLD, &size); 13 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 14 | 15 | bool version2 = false; 16 | 17 | // version 2 18 | if (version2) { 19 | int rank_s = rank; 20 | 21 | if (rank>0) { 22 | MPI_Send(&rank_s, 1, MPI_INT, 0, 0, MPI_COMM_WORLD); 23 | } else { // you are process 0 24 | std::string message = (argc > 1) ? std::string(argv[1]) : "world"; 25 | std::cout << "Hello " + message + ", from rank " + std::to_string(rank_s) << " of size " + std::to_string(size) + "." << std::endl; 26 | for (int r=1; r 1) ? std::string(argv[1]) : "world"; 35 | const std::string message = "Hello " + greeting + ", from rank " + std::to_string(rank) + " of size " + std::to_string(size) + "."; 36 | 37 | if (rank>0) { 38 | MPI_Send(message.c_str(), message.length(), MPI_CHAR, 0, 0, MPI_COMM_WORLD); 39 | } else { 40 | std::cout << message << std::endl; 41 | char * buf = (char *) malloc(message.length()+1); // create a buffer to receive, a string can just be sent not received 42 | for (int r=1; r 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | std::array compute_gradient( 12 | const std::function &)> &f, 13 | const std::array &x, double h) { 14 | // initialize gradient 15 | // FILL HERE 16 | 17 | // get rank and size 18 | // FILL HERE 19 | 20 | // subdivide computations by processor 21 | for (/*FILL HERE*/) { 22 | auto x_plus_h(x), x_minus_h(x); 23 | x_plus_h[i] += h; 24 | x_minus_h[i] -= h; 25 | gradient[i] = (f(x_plus_h) - f(x_minus_h)) / 2.0 / h; 26 | } 27 | 28 | // send result to main proc 29 | for (size_t i = 0; i < N; ++i) { // for each component of the gradient 30 | const int belongs_to_proc = 31 | /*FILL HERE: compute to which proc it belongs to*/; 32 | if (belongs_to_proc /*!= 0*/) { // then we need to send it to proc 0 33 | if (/*FILL HERE: belong to us*/) { 34 | // FILL HERE: we send it to proc 0 35 | } else if (/*FILL HERE: we are proc 0*/) { 36 | // FILL HERE: we receive the data 37 | } 38 | } 39 | } 40 | return gradient; 41 | } 42 | 43 | int main(int argc, char *argv[]) { 44 | // init MPI, get rank and size 45 | // FILL HERE 46 | 47 | // define types 48 | using Vector = std::array; 49 | using Function = std::function; 50 | // define the field f of which we compute the gradient at the point where we 51 | // evaluate it 52 | const Function f = [](auto v) { 53 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 54 | 2 * v[3] * v[3] * v[3]; 55 | }; 56 | const Vector y = {0, 1, std::numbers::pi, 2}; 57 | // define the correct value of the gradient 58 | const Vector g_true = {1, std::exp(1), 0, 24}; 59 | // compute the gradient with our function 60 | const Vector g = compute_gradient(f, y, 1e-8); 61 | 62 | // check if the solution is correct 63 | if (/*FILL HERE*/) { 64 | std::cout << std::setprecision(4) << std::scientific; 65 | for (size_t i = 0; i < g.size(); ++i) { 66 | const auto err = std::abs(g[i] - g_true[i]); 67 | std::cout << i << " | " << g[i] << (err < 1e-6 ? " PASS" : " FAIL") 68 | << std::endl; 69 | } 70 | } 71 | // FILL HERE: finalize 72 | return 0; 73 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab05-mpi/ex02/serial.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | std::array compute_gradient( 10 | const std::function &)> &f, 11 | const std::array &x, double h) { 12 | std::array gradient; 13 | 14 | for (size_t i = 0; i < N; i++) { 15 | auto x_plus_h(x), x_minus_h(x); 16 | x_plus_h[i] += h; 17 | x_minus_h[i] -= h; 18 | gradient[i] = (f(x_plus_h) - f(x_minus_h)) / 2.0 / h; 19 | } 20 | return gradient; 21 | } 22 | 23 | int main() { 24 | // define types 25 | using Vector = std::array; 26 | using Function = std::function; 27 | 28 | // define the field f of which we compute the gradient at the point where we 29 | // evaluate it 30 | const Function f = [](auto v) { 31 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 32 | 2 * v[3] * v[3] * v[3]; 33 | }; 34 | const Vector y = {0, 1, std::numbers::pi, 2}; 35 | // define the correct value of the gradient 36 | const Vector g_true = {1, std::exp(1), 0, 24}; 37 | // compute the gradient with our function 38 | const Vector g = compute_gradient(f, y, 1e-8); 39 | 40 | // check if the result is correct 41 | std::cout << std::setprecision(4) << std::scientific; 42 | for (size_t i = 0; i < g.size(); ++i) { 43 | const auto err = std::abs(g[i] - g_true[i]); 44 | std::cout << i << " | " << g[i] << (err < 1e-6 ? " PASS" : " FAIL") 45 | << std::endl; 46 | } 47 | 48 | return 0; 49 | } -------------------------------------------------------------------------------- /Labs/2024-25/lab05-mpi/ex02/solution.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | std::array compute_gradient( 12 | const std::function &)> &f, 13 | const std::array &x, double h) { 14 | // initialize gradient 15 | std::array gradient; 16 | 17 | // get rank and size 18 | int rank, size; 19 | MPI_Comm_size(MPI_COMM_WORLD, &size); 20 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 21 | 22 | // subdivide computations by processor 23 | for (size_t i = rank; i < N; i += size) { 24 | auto x_plus_h(x), x_minus_h(x); 25 | x_plus_h[i] += h; 26 | x_minus_h[i] -= h; 27 | gradient[i] = (f(x_plus_h) - f(x_minus_h)) / 2.0 / h; 28 | } 29 | 30 | // send result to main proc 31 | for (size_t i = 0; i < N; ++i) { 32 | const int belongs_to_proc = i % size; 33 | const int tag = i / size; 34 | if (belongs_to_proc /*!= 0*/) { 35 | if (rank == belongs_to_proc) { 36 | MPI_Send(&gradient[i], 1, MPI_DOUBLE, 0, tag, MPI_COMM_WORLD); 37 | } else if (rank == 0) { 38 | MPI_Recv(&gradient[i], 1, MPI_DOUBLE, belongs_to_proc, tag, 39 | MPI_COMM_WORLD, MPI_STATUS_IGNORE); 40 | } 41 | } 42 | } 43 | return gradient; 44 | } 45 | 46 | int main(int argc, char *argv[]) { 47 | // init MPI 48 | MPI_Init(&argc, &argv); 49 | int rank, size; 50 | MPI_Comm_size(MPI_COMM_WORLD, &size); 51 | MPI_Comm_rank(MPI_COMM_WORLD, &rank); 52 | 53 | // define types 54 | using Vector = std::array; 55 | using Function = std::function; 56 | // define the field f of which we compute the gradient at the point where we 57 | // evaluate it 58 | const Function f = [](auto v) { 59 | return std::sin(v[0]) + std::exp(v[1]) + std::cos(v[2]) + 60 | 2 * v[3] * v[3] * v[3]; 61 | }; 62 | const Vector y = {0, 1, std::numbers::pi, 2}; 63 | // define the correct value of the gradient 64 | const Vector g_true = {1, std::exp(1), 0, 24}; 65 | // compute the gradient with our function 66 | const Vector g = compute_gradient(f, y, 1e-8); 67 | 68 | // check if the solution is correct 69 | if (rank == 0) { 70 | std::cout << std::setprecision(4) << std::scientific; 71 | for (size_t i = 0; i < g.size(); ++i) { 72 | const auto err = std::abs(g[i] - g_true[i]); 73 | std::cout << i << " | " << g[i] << (err < 1e-6 ? " PASS" : " FAIL") 74 | << std::endl; 75 | } 76 | } 77 | MPI_Finalize(); 78 | return 0; 79 | } -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/01-compilation-unit/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY = clean 2 | 3 | main: main.o sum.o 4 | 5 | main.o: main.cpp 6 | sum.o: sum.cpp 7 | 8 | clean: 9 | rm -f main 10 | rm -f *.o -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/01-compilation-unit/main.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" 2 | // problem: no header guard 3 | // solvable also with inline, however it is not 4 | // a good practice to use inline if the function is long 5 | // moreover we are not exploiting the source file 6 | int main() { 7 | sum(3, 3); 8 | return 0; 9 | } -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/01-compilation-unit/sum.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" 2 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/01-compilation-unit/sum.hpp: -------------------------------------------------------------------------------- 1 | double sum(double a, double b) { 2 | return a + b; 3 | } -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/02-compilation-unit/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY = clean 2 | 3 | main: main.o sum.o 4 | 5 | main.o: main.cpp 6 | sum.o: sum.cpp 7 | 8 | clean: 9 | rm -f main 10 | rm -f *.o -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/02-compilation-unit/main.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" 2 | // problem: no definition of sum 3 | int main() { 4 | sum(5, 4); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/02-compilation-unit/sum.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" 2 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/02-compilation-unit/sum.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SUM_HPP 2 | #define SUM_HPP 3 | 4 | double sum(double a, double b); 5 | 6 | #endif -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/03-compilation-unit/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY = clean 2 | 3 | main: main.o sum.o 4 | 5 | main.o: main.cpp 6 | sum.o: sum.cpp 7 | 8 | clean: 9 | rm -f main 10 | rm -f *.o -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/03-compilation-unit/main.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" 2 | // problem: multiple definitions 3 | double sum(double a, double b) { 4 | return a + b; 5 | } 6 | 7 | int main() { 8 | sum(5, 4); 9 | return 0; 10 | } -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/03-compilation-unit/sum.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" 2 | double sum(double a, double b) { 3 | return a + b; 4 | } 5 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/03-compilation-unit/sum.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SUM_HPP 2 | #define SUM_HPP 3 | 4 | double sum(double a, double b); 5 | 6 | #endif -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/04-compilation-unit/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY = clean 2 | 3 | main: main.o sum.o 4 | 5 | main.o: main.cpp 6 | sum.o: sum.cpp 7 | 8 | clean: 9 | rm -f *.o 10 | rm -f main -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/04-compilation-unit/main.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" 2 | 3 | // why it works? methos defined in the body are inlined 4 | 5 | int main() { 6 | Sum s; 7 | s.do_sum(5, 4); 8 | return 0; 9 | } -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/04-compilation-unit/sum.cpp: -------------------------------------------------------------------------------- 1 | #include "sum.hpp" -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/04-compilation-unit/sum.hpp: -------------------------------------------------------------------------------- 1 | struct Sum { 2 | double do_sum(double a, double b) { return a + b; } 3 | }; -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/05-inheritance/ex01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class Base { 5 | public: 6 | ~Base() { std::cout << "Bye from base\n"; }; // Non-virtual. 7 | }; 8 | 9 | class Derived : public Base { 10 | public: 11 | ~Derived() { std::cout << "Bye from derived\n"; } 12 | }; 13 | 14 | int main () { 15 | std::unique_ptr p = std::make_unique(); 16 | return 0; 17 | }; -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/05-inheritance/ex02.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using BaseMatrix = std::vector; 7 | 8 | class DenseMatrix : public BaseMatrix { 9 | public: 10 | DenseMatrix(size_t n) : BaseMatrix(n) { 11 | for(auto &e : *this) 12 | e = new double[n]; 13 | } 14 | ~DenseMatrix() { 15 | std::cout << "Bye from DenseMatrix\n"; 16 | for(auto &e : *this) { 17 | if(e) { 18 | delete[] e; 19 | } 20 | } 21 | } 22 | }; 23 | 24 | class SparseMatrix : public BaseMatrix { 25 | public: 26 | SparseMatrix(size_t n) : BaseMatrix(n) { 27 | for(auto &e : *this) 28 | // allocate constant memory for each row, 29 | // for instance for FEM matrices constant memory is enough 30 | e = new double[5]; 31 | } 32 | ~SparseMatrix() { 33 | std::cout << "Bye from SparseMatrix\n"; 34 | for(auto &e : *this) { 35 | if(e) { 36 | delete[] e; 37 | } 38 | } 39 | } 40 | }; 41 | 42 | void do_something(const std::unique_ptr &p) { 43 | // call some method of p 44 | } 45 | 46 | int main () { 47 | std::unique_ptr p = std::make_unique(10); 48 | do_something(p); 49 | return 0; 50 | }; -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/00-examples/compileWithPreProc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/twoexercises/05-stl-templates/00-examples/compileWithPreProc.png -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/01-sparse-matrix/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | CXXFLAGS ?= -std=c++17 3 | CPPFLAGS ?= -O3 -Wall -pedantic -I. 4 | LDLIBS ?= 5 | LINK.o := $(LINK.cc) # Use C++ linker. 6 | 7 | DEPEND = make.dep 8 | 9 | EXEC = main 10 | SRCS = $(wildcard *.cpp) 11 | OBJS = $(SRCS:.cpp=.o) 12 | 13 | .PHONY = all $(EXEC) $(OBJS) clean distclean $(DEPEND) 14 | 15 | all: $(DEPEND) $(EXEC) 16 | 17 | $(EXEC): $(OBJS) 18 | 19 | $(OBJS): %.o: %.cpp 20 | 21 | clean: 22 | $(RM) $(DEPEND) 23 | $(RM) *.o 24 | 25 | distclean: clean 26 | $(RM) $(EXEC) 27 | $(RM) *.csv *.out *.bak *~ 28 | 29 | $(DEPEND): $(SRCS) 30 | @$(RM) $(DEPEND) 31 | @for file in $(SRCS); \ 32 | do \ 33 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MM $${file} >> $(DEPEND); \ 34 | done 35 | 36 | -include $(DEPEND) 37 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/01-sparse-matrix/main.cpp: -------------------------------------------------------------------------------- 1 | #include "sparse_matrix.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int main(int argc, char **argv) { 9 | 10 | // set matrix dimensions 11 | const unsigned int n = 10; 12 | 13 | // declare sparse matrix obj 14 | SparseMatrix A; 15 | 16 | // resize, acts on the number of rows 17 | A.resize(n); 18 | 19 | // populate A 20 | for (unsigned int i = 0; i < n; ++i) 21 | { 22 | if (i > 0) 23 | A[i][i - 1] = -1; 24 | 25 | if (i < n - 1) 26 | A[i][i + 1] = -1; 27 | 28 | A[i][i] = 4; 29 | } 30 | 31 | // return A 32 | std::cout << "Loop through matrix entries:" << std::endl; 33 | for (unsigned int i = 0; i < n; ++i) 34 | for (const auto &[j, val] : A[i]) 35 | std::cout << "A[" << i << "][" << j << "] = " << val << std::endl; 36 | std::cout << std::endl << std::endl; 37 | 38 | // return A with stream operator 39 | std::cout << "Stream operator:" << std::endl; 40 | std::cout << A; 41 | std::cout << std::endl; 42 | 43 | std::vector val; 44 | std::vector col_ind, row_ptr; 45 | 46 | // csr format 47 | A.csr(val, col_ind, row_ptr); 48 | 49 | std::cout << "CSR vectors:" << std::endl; 50 | for (auto i : val) 51 | std::cout << i << " "; 52 | std::cout << std::endl; 53 | 54 | for (auto i : col_ind) 55 | std::cout << i << " "; 56 | std::cout << std::endl; 57 | 58 | for (auto i : row_ptr) 59 | std::cout << i << " "; 60 | std::cout << std::endl << std::endl; 61 | 62 | std::cout << "Matrix entries from CSR:" << std::endl; 63 | for (unsigned int i = 0; i < n; ++i) 64 | for (unsigned int j = row_ptr[i]; j < row_ptr[i + 1]; ++j) 65 | std::cout << "A[" << i << "][" << col_ind[j] << "] = " << val[j] 66 | << std::endl; 67 | std::cout << std::endl; 68 | 69 | // test matrix vector product 70 | std::cout << "Matrix-vector product:" << std::endl; 71 | const std::vector x(n, 1.0); 72 | const std::vector y = A * x; 73 | for (const auto i : y) 74 | std::cout << i << " "; 75 | std::cout << std::endl << std::endl; 76 | } 77 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/01-sparse-matrix/sparse_matrix.cpp: -------------------------------------------------------------------------------- 1 | #include "sparse_matrix.hpp" 2 | 3 | template 4 | void SparseMatrix::update_properties() { 5 | } 6 | 7 | 8 | template 9 | std::ostream &operator<<(std::ostream &stream, SparseMatrix &M) { 10 | } 11 | 12 | template 13 | void 14 | SparseMatrix::aij(std::vector &a, 15 | std::vector &i, 16 | std::vector &j) { 17 | } 18 | 19 | template 20 | void 21 | SparseMatrix::csr(std::vector &val, 22 | std::vector &col_ind, 23 | std::vector &row_ptr) 24 | { 25 | } 26 | 27 | template 28 | void 29 | SparseMatrix::operator+=(SparseMatrix &other) 30 | { 31 | } 32 | 33 | template 34 | std::vector 35 | operator*(SparseMatrix &M, const std::vector &x) 36 | { 37 | } 38 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/01-sparse-matrix/sparse_matrix.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPARSE_MATRIX_HPP 2 | #define SPARSE_MATRIX_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | template 9 | using RowType = std::map; 10 | 11 | /// Templated class for sparse row-oriented matrix. 12 | template 13 | class SparseMatrix { 14 | public: 15 | /// Default constructor. 16 | SparseMatrix() 17 | : m_nnz(0) 18 | , m_m(0) 19 | {} 20 | 21 | /// Implement usefull methods exploiting std::vector 22 | inline size_t size() const { 23 | return m_data.size(); 24 | } 25 | 26 | inline void resize(size_t n) { 27 | m_data.resize(n); 28 | } 29 | 30 | // operator[] in const and not const version, 31 | // C++ chooses the best overload 32 | inline const RowType &operator[](size_t n) const { 33 | return m_data[n]; 34 | } 35 | 36 | inline RowType &operator[](size_t n) { 37 | return m_data[n]; 38 | } 39 | 40 | /// Recompute sparse matrix properties. 41 | void update_properties(); 42 | 43 | /// Sparse matrix sum. Automatically allocates additional entries. 44 | void operator+=(SparseMatrix &other); 45 | 46 | /// Stream operator. 47 | template 48 | friend std::ostream & 49 | operator<<(std::ostream &stream, SparseMatrix &M); 50 | 51 | /// Compute matrix-vector product. 52 | template 53 | friend std::vector 54 | operator*(SparseMatrix &M, const std::vector &x); 55 | 56 | /// Convert row-oriented sparse matrix to AIJ format. 57 | void 58 | aij(std::vector &a, 59 | std::vector &i, 60 | std::vector &j); 61 | 62 | /// Convert row-oriented sparse matrix to CSR format. 63 | void 64 | csr(std::vector &val, 65 | std::vector &col_ind, 66 | std::vector &row_ptr); 67 | 68 | private: 69 | std::vector> m_data; 70 | size_t m_nnz; ///< Number of nonzero elements. 71 | size_t m_m; ///< Number of nonempty columns. 72 | }; 73 | 74 | #endif /* SPARSE_MATRIX_HPP */ 75 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/02-derivatives/Derivatives.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DERIVATIVES_HPP 2 | #define DERIVATIVES_HPP 3 | 4 | #include 5 | #include 6 | 7 | using func_t = std::function; 8 | 9 | 10 | class NaiveNthDerivative { 11 | public: 12 | NaiveNthDerivative(const func_t &f, double h, unsigned int deg) : 13 | m_f(f), 14 | m_h(h), 15 | m_deg(deg) {} 16 | double operator()(double x) { 17 | } 18 | private: 19 | double f_prime(double x, unsigned deg) { 20 | } 21 | 22 | const func_t m_f; 23 | const double m_h; 24 | const unsigned int m_deg; 25 | }; 26 | 27 | #endif /* DERIVATIVES_HPP */ 28 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/02-derivatives/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | CXXFLAGS ?= -std=c++17 3 | CPPFLAGS ?= -O3 -Wall -pedantic -I. 4 | LDLIBS ?= 5 | LINK.o := $(LINK.cc) # Use C++ linker. 6 | 7 | DEPEND = make.dep 8 | 9 | EXEC = main 10 | SRCS = $(wildcard *.cpp) 11 | OBJS = $(SRCS:.cpp=.o) 12 | 13 | .PHONY = all $(EXEC) $(OBJS) clean distclean $(DEPEND) 14 | 15 | all: $(DEPEND) $(EXEC) 16 | 17 | $(EXEC): $(OBJS) 18 | 19 | $(OBJS): %.o: %.cpp 20 | 21 | clean: 22 | $(RM) $(DEPEND) 23 | $(RM) *.o 24 | 25 | distclean: clean 26 | $(RM) $(EXEC) 27 | $(RM) *.csv *.out *.bak *~ 28 | 29 | $(DEPEND): $(SRCS) 30 | @$(RM) $(DEPEND) 31 | @for file in $(SRCS); \ 32 | do \ 33 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MM $${file} >> $(DEPEND); \ 34 | done 35 | 36 | -include $(DEPEND) 37 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/02-derivatives/main.cpp: -------------------------------------------------------------------------------- 1 | #include "Derivatives.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | auto timeit(const std::function &f, size_t N = 1) { 9 | using namespace std::chrono; 10 | const auto t0 = high_resolution_clock::now(); 11 | for(size_t i = 0; i < N; ++i) 12 | f(); 13 | const auto t1 = high_resolution_clock::now(); 14 | return duration_cast(t1 - t0).count(); 15 | } 16 | 17 | 18 | int main(int argc, char **argv) { 19 | const size_t N = 1; 20 | const double h = 1e-2; 21 | 22 | const auto f = [](double x) { return x*x*x*x*x; }; 23 | const auto df4 = [](double x) { return 5.0*4.0*3.0*2.0*x; }; 24 | 25 | const auto test_manual = [&]() { 26 | const auto y = df4(1.0); 27 | }; 28 | 29 | std::cout << "Test hard coded: " << timeit(test_manual, N) << "[ms]\n"; 30 | } 31 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/05-stl-templates/doc/05-stl-templates.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/twoexercises/05-stl-templates/doc/05-stl-templates.pdf -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/doc/06-eigen-newton.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/twoexercises/06-eigen-newton/doc/06-eigen-newton.pdf -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/doc/IntroToEigen.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/twoexercises/06-eigen-newton/doc/IntroToEigen.pdf -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/doc/ipe/class.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/twoexercises/06-eigen-newton/doc/ipe/class.pdf -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/doc/ipe/newton.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HPC-Courses/AMSC-Labs/4318e94e8e0ff10babfa961d305b37e1dca8a242/Labs/2024-25/twoexercises/06-eigen-newton/doc/ipe/newton.pdf -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main(){ 5 | // use eigen::vector for compability with eigen 6 | Eigen::VectorXd b(5); 7 | 8 | // stream operator to define the input 9 | b << 1.5, 3, 2.2, 0, 10; 10 | 11 | // overload of call operator for 12 | b(0) = 0; 13 | b(2) = 2; 14 | // ... 15 | 16 | Eigen::MatrixXd A(5,5); // nrows, ncols 17 | 18 | // you can modify the columns with the keyword col: 19 | A.col(0) = b; 20 | 21 | for (size_t i=0; i < A.rows(); i++) 22 | for (size_t j=0; j < A.rows(); j++) 23 | A(i,j) = 1 / (i+j+1.0); 24 | 25 | std::cout << A*b << std::endl; 26 | 27 | std::cout << A.fullPivLu().solve(b) << std::endl; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/Jacobian.cpp: -------------------------------------------------------------------------------- 1 | #include "Jacobian.hpp" 2 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/Jacobian.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JACOBIAN_HPP 2 | #define JACOBIAN_HPP 3 | 4 | #include "NewtonTraits.hpp" 5 | 6 | /// The base class for representing the Jacobian. 7 | /// 8 | /// It implements the basic methods for the application of the 9 | /// Jacobian, which in fact may be a "pseudo-Jacobian" for 10 | /// quasi-Newton methods or even Picard iterations. 11 | class JacobianBase 12 | { 13 | public: 14 | /// Short-hand alias. 15 | using T = NewtonTraits; 16 | 17 | /// Default constructor. 18 | JacobianBase() = default; 19 | 20 | /// Solve J(x) * delta_x = res. 21 | virtual T::VariableType 22 | solve(const T::VariableType &x, const T::VariableType &res) const = 0; 23 | 24 | /// Destructor. Needed since this is a pure virtual class. 25 | virtual ~JacobianBase() = default; 26 | }; 27 | 28 | #endif /* JACOBIAN_HPP */ 29 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/JacobianFactory.hpp: -------------------------------------------------------------------------------- 1 | #ifndef JACOBIANFACTORY_HPP 2 | #define JACOBIANFACTORY_HPP 3 | 4 | #endif /* JACOBIANFACTORY_HPP */ 5 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/Makefile: -------------------------------------------------------------------------------- 1 | CXX ?= g++ 2 | CXXFLAGS ?= -std=c++17 3 | CPPFLAGS ?= -O3 -Wall -pedantic -I. -I${mkEigenInc} 4 | LDLIBS ?= 5 | LINK.o := $(LINK.cc) # Use C++ linker. 6 | 7 | DEPEND = make.dep 8 | 9 | EXEC = main 10 | SRCS = $(wildcard *.cpp) 11 | OBJS = $(SRCS:.cpp=.o) 12 | 13 | .PHONY = all $(EXEC) $(OBJS) clean distclean $(DEPEND) 14 | 15 | all: $(DEPEND) $(EXEC) 16 | 17 | $(EXEC): $(OBJS) 18 | 19 | $(OBJS): %.o: %.cpp 20 | 21 | clean: 22 | $(RM) $(DEPEND) 23 | $(RM) *.o 24 | 25 | distclean: clean 26 | $(RM) $(EXEC) 27 | $(RM) *.csv *.out *.bak *~ 28 | 29 | $(DEPEND): $(SRCS) 30 | @$(RM) $(DEPEND) 31 | @for file in $(SRCS); \ 32 | do \ 33 | $(CXX) $(CPPFLAGS) $(CXXFLAGS) -MM $${file} >> $(DEPEND); \ 34 | done 35 | 36 | -include $(DEPEND) 37 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/Newton.cpp: -------------------------------------------------------------------------------- 1 | #include "Newton.hpp" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | NewtonResult 8 | Newton::solve(const T::VariableType &x0) 9 | { 10 | // C++17 structured binding. 11 | // Get all options and result. 12 | const auto &[tol_res, tol_incr, max_iter, stop_on_stagnation] = this->options; 13 | 14 | auto &[solution, norm_res, norm_incr, iteration, converged, stagnation] = 15 | this->result; 16 | 17 | // The initial step is to compute the relevant quantities 18 | // from the initial conditions. 19 | solution = x0; 20 | iteration = 0; 21 | norm_res = std::numeric_limits::max(); 22 | norm_incr = std::numeric_limits::max(); 23 | 24 | auto residual = this->system(solution); 25 | 26 | // Test if we have a map Rn -> Rn. 27 | if (solution.size() != residual.size()) 28 | throw std::runtime_error("Newton needs a function from Rn to Rn"); 29 | 30 | norm_res = residual.norm(); 31 | 32 | // Initial values for tests of convergence and stagnation. 33 | converged = false; 34 | stagnation = false; 35 | 36 | bool stop = true; 37 | bool no_decrease_old = true; 38 | 39 | for (iteration = 0; iteration < max_iter; ++iteration) 40 | { 41 | auto norm_res_old = norm_res; 42 | 43 | // Compute the increment. 44 | auto delta = jac->solve(solution, residual); 45 | 46 | norm_incr = delta.norm(); 47 | solution -= delta; 48 | 49 | residual = this->system(solution); 50 | 51 | norm_res = residual.norm(); 52 | 53 | // If residual does not decrease for two consecutive iterations 54 | // mark for stagnation. 55 | const bool no_decrease = (norm_res >= norm_res_old); 56 | 57 | stagnation = (no_decrease_old && no_decrease); 58 | 59 | // We stop on stagnation if we decided to. 60 | stop = (stagnation && stop_on_stagnation); 61 | 62 | // Test convergence. 63 | converged = ((norm_res <= tol_res) && (norm_incr <= tol_incr)); 64 | 65 | std::cout << " Iteration " << iteration; 66 | std::cout << ", residual: " << norm_res; 67 | std::cout << ", increment: " << norm_incr; 68 | std::cout << std::endl; 69 | 70 | if (converged || stop) 71 | break; 72 | 73 | no_decrease_old = no_decrease; 74 | } 75 | 76 | return result; 77 | } 78 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/Newton.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NEWTON_HPP 2 | #define NEWTON_HPP 3 | 4 | #include "Jacobian.hpp" 5 | #include "NewtonMethodsSupport.hpp" 6 | 7 | #include 8 | 9 | /// This class implements Newton's method. 10 | /// 11 | /// The non-linear system to be solved is composed to this class as 12 | /// a private member, whereas the Jacobian is stored as a unique_ptr 13 | /// to the base class. However, as a result the class is neither 14 | /// copy-constructible nor copy-assignable. To have copy operators that perform 15 | /// deep copy a clone() method should be implemented in the Jacobian classes. 16 | class Newton 17 | { 18 | public: 19 | /// Short-hand alias. 20 | using T = NewtonTraits; 21 | 22 | /// Default constructor. 23 | Newton() = default; 24 | 25 | /// This class is not meant to be copy constructable. 26 | /// The implementation of copy constructor would require a 27 | /// mechanism for the deep copy of the Jacobian, which is stored 28 | /// as a unique pointer. 29 | Newton(const Newton &) = delete; 30 | 31 | /// This constructor accepts a non linear system and a unique 32 | /// pointer to the Jacobian base class. 33 | Newton(T::NonLinearSystemType && system_, 34 | std::unique_ptr jac_, 35 | const NewtonOptions & options_ = NewtonOptions()) 36 | : system(std::forward(system_)) 37 | , jac(std::move(jac_)) 38 | , options(options_) 39 | {} 40 | 41 | template 42 | Newton(T::NonLinearSystemType &&system_, 43 | Jac jac_, 44 | const NewtonOptions & options_ = NewtonOptions()) 45 | : system(std::forward(system_)) 46 | , jac(std::make_unique(jac_)) 47 | , options(options_) 48 | {} 49 | 50 | virtual ~Newton() = default; 51 | 52 | /// You can set options. 53 | void 54 | set_options(const NewtonOptions &options_) 55 | { 56 | options = options_; 57 | } 58 | 59 | NewtonResult 60 | solve(const T::VariableType &x0); 61 | 62 | private: 63 | T::NonLinearSystemType system; 64 | std::unique_ptr jac; 65 | NewtonOptions options; 66 | NewtonResult result; 67 | }; 68 | 69 | 70 | #endif /* NEWTON_HPP_ */ 71 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/NewtonMethodsSupport.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NEWTONMETHODSSUPPORT_HPP 2 | #define NEWTONMETHODSSUPPORT_HPP 3 | 4 | /// Newton solver options, with default values. 5 | /// 6 | /// @note Absolute tolerances are used in the code. 7 | /// It is user's task to set them properly. 8 | class NewtonOptions 9 | { 10 | public: 11 | /// Tolerance on residual. 12 | /// The iteration stops if @f$||F(x)|| < tol_res@f$. 13 | double tol_res = 1e-6; 14 | 15 | /// Tolerance on increment. 16 | /// The method stops if 17 | /// @f$||x_{new}-x_{old}|| < tol_incr@f$. 18 | double tol_incr = 1e-8; 19 | 20 | /// Max. number of iterations. 21 | unsigned int max_iter = 50; 22 | 23 | /// Stop if stagnation occurs, i.e. if for two consecutive 24 | /// iterations the residual does not decrease. 25 | bool stop_on_stagnation = false; 26 | }; 27 | 28 | /// Output results. 29 | class NewtonResult 30 | { 31 | public: 32 | /// Solution. 33 | NewtonTraits::VariableType solution; 34 | 35 | /// Residual norm. 36 | double norm_res = 0.0; 37 | 38 | /// Increment norm. 39 | double norm_incr = 0.0; 40 | 41 | /// Iteration counter. 42 | unsigned int iteration = 0; 43 | 44 | /// Convergence flag. True if convergence is reached, i.e. 45 | /// if both conditions on residual and increment are met. 46 | bool converged = false; 47 | 48 | /// Stagnation flag. True if stagnation occurred. 49 | bool stagnation = false; 50 | }; 51 | 52 | #endif /* NEWTONMETHODSSUPPORT_HPP */ 53 | -------------------------------------------------------------------------------- /Labs/2024-25/twoexercises/06-eigen-newton/src/NewtonTraits.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NEWTONTRAITS_HPP 2 | #define NEWTONTRAITS_HPP 3 | 4 | #endif /* NEWTONTRAITS_HPP */ 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AMSC-Labs 2 | The laboratory sessions for the AMSC Course for Master students in High-Performance Computing and Big Data at Politecnico Milano, organised by year. 3 | 4 | You can clone it on your PC with (if you have a github account with ssh keys recorded) 5 | ``` 6 | git clone git@github.com:HPC-Courses/AMSC-Labs.git 7 | ``` 8 | or (discouraged) 9 | ``` 10 | git clone https://github.com/HPC-Courses/AMSC-Labs.git 11 | ``` 12 | Enjoy! 13 | --------------------------------------------------------------------------------