From 35367d151b1fd6f8042977d1655bb2ddee67ce7f Mon Sep 17 00:00:00 2001 From: Cassandra Masschelein <49848281+cassmasschelein@users.noreply.github.com> Date: Tue, 12 Mar 2024 12:00:15 -0400 Subject: [PATCH] Git workflow and pre-commit updates (#21) * added cpp check to pr workflow * testing pre-commit hooks * added pre-commit hooks for cpp formatting * added cpp pre-commit hook, updated readme * Added pre-commit hooks for cpp code analysis * update requirements * update github workflows * update github workflows * update github workflows * update github workflows * wip * wip * Fix C++ files from cppcheck --------- Co-authored-by: Michelle Richer --- .pre-commit-config.yaml | 95 ++++++++++------- README.md | 3 + permanent/__init__.py | 1 - src/permanent.cc | 60 ++++++++--- src/permanent.h | 219 +++++++++++++++++----------------------- src/py_permanent.cc | 154 ++++++++++++++-------------- src/tables.h | 101 +++++------------- src/tuning.cc | 159 ++++++++++++++--------------- tools/tuning.py | 14 +-- 9 files changed, 387 insertions(+), 419 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 669bb11..bc17c38 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,38 +1,59 @@ +exclude: | + (?x)^( + src/perm-mv0\.h$ | + src/kperm-gray\.h$ | + src/swap\.h$ + ) + +ci: + skip: + [cppcheck, cpplint, clang-format] + repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 - hooks: - - id: check-added-large-files # prevents giant files from being commit - - id: check-ast # Check whether files parse as valid python. - - id: check-case-conflict # Checks conflict case-insensitive - - id: check-executables-have-shebangs - - id: check-json - - id: check-merge-conflict - - id: check-symlinks # Check for symlinks which don't point - - id: check-toml - - id: check-vcs-permalinks - - id: debug-statements # Check for debugger imports and py37 breakpt - - id: detect-private-key # Checks for the existence of private keys. - - id: destroyed-symlinks - - id: end-of-file-fixer # Makes sure files end in a newline - - id: fix-byte-order-marker # Removes UTF-8 byte order marker - - id: mixed-line-ending # Replaces or checks mixed line ending. - - id: pretty-format-json # Checks JSON are pretty - args: ["--autofix", "--no-sort-keys"] - - id: trailing-whitespace # Trims trailing whitespace. -- repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.5.4 - hooks: - - id: remove-crlf -- repo: https://github.com/psf/black - rev: 23.12.1 - hooks: - - id: black -- repo: https://github.com/astral-sh/ruff-pre-commit - rev: 'v0.1.9' - hooks: - - id: ruff -- repo: https://github.com/python-jsonschema/check-jsonschema - rev: 0.27.3 - hooks: - - id: check-github-workflows + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: check-added-large-files # prevents giant files from being commit + - id: check-ast # Check whether files parse as valid python. + - id: check-case-conflict # Checks conflict case-insensitive + - id: check-executables-have-shebangs + - id: check-json + - id: check-merge-conflict + - id: check-symlinks # Check for symlinks which don't point + - id: check-toml + - id: check-vcs-permalinks + - id: debug-statements # Check for debugger imports and py37 breakpt + - id: detect-private-key # Checks for the existence of private keys + - id: destroyed-symlinks + - id: end-of-file-fixer # Makes sure files end in a newline + - id: fix-byte-order-marker # Removes UTF-8 byte order marker + - id: mixed-line-ending # Replaces or checks mixed line ending + - id: pretty-format-json # Checks JSON are pretty + args: ["--autofix", "--no-sort-keys"] + - id: trailing-whitespace # Trims trailing whitespace + - repo: https://github.com/Lucas-C/pre-commit-hooks + rev: v1.5.5 + hooks: + - id: remove-crlf + - repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.3.2 + hooks: + - id: ruff + - repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.28.0 + hooks: + - id: check-github-workflows + - repo: https://github.com/pocc/pre-commit-hooks + rev: v1.3.5 + hooks: + - id: cppcheck + args: ["--inline-suppr", "--suppress=missingIncludeSystem", + "--suppress=*:src/swap.h", "--suppress=*:src/perm-mv0.h", "--suppress=*:src/kperm-gray.h"] + - id: cpplint + args: ["--root=src", "--filter=-build/include_subdir"] + - id: clang-format + args: ["-i", "-style={BasedOnStyle: google, IndentWidth: 4}"] diff --git a/README.md b/README.md index 10987f7..2376e27 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,8 @@ [![Python 3](http://img.shields.io/badge/python-3-blue.svg)](https://docs.python.org/3/) [![gcc](https://img.shields.io/badge/-C++-blue?logo=cplusplus)](https://gcc.gnu.org/) +[![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/theochem/matrix-permanent/actions/workflows/pull_request.yml) +[![GNU GPLv3](https://img.shields.io/badge/license-%20%20GNU%20GPLv3%20-green?style=plastic)](https://www.gnu.org/licenses/gpl-3.0.en.html) + # Permanent diff --git a/permanent/__init__.py b/permanent/__init__.py index 22da8dc..c0a21fd 100644 --- a/permanent/__init__.py +++ b/permanent/__init__.py @@ -1,6 +1,5 @@ r"""Module for computing the permanents of matrices.""" - __all__ = [ "opt", "combinatoric", diff --git a/src/permanent.cc b/src/permanent.cc index 5d5dc17..83207fc 100644 --- a/src/permanent.cc +++ b/src/permanent.cc @@ -1,20 +1,48 @@ -#include +/* Copyright 2034 QC-Devs (GPLv3) */ #include "permanent.h" +#include + +template double combinatoric(const std::size_t, const std::size_t, + const double *); + +template double combinatoric_rectangular(const std::size_t, + const std::size_t, + const double *); + +template double glynn(const std::size_t, const std::size_t, + const double *); + +template double glynn_rectangular(const std::size_t, const std::size_t, + const double *); + +template double ryser(const std::size_t, const std::size_t, + const double *); + +template double ryser_rectangular(const std::size_t, const std::size_t, + const double *); + +template double opt(const std::size_t, const std::size_t, + const double *); + +template std::complex combinatoric>( + const std::size_t, const std::size_t, const std::complex *); + +template std::complex combinatoric_rectangular>( + const std::size_t, const std::size_t, const std::complex *); + +template std::complex glynn>( + const std::size_t, const std::size_t, const std::complex *); + +template std::complex glynn_rectangular>( + const std::size_t, const std::size_t, const std::complex *); + +template std::complex ryser>( + const std::size_t, const std::size_t, const std::complex *); + +template std::complex ryser_rectangular>( + const std::size_t, const std::size_t, const std::complex *); -template double combinatoric(const std::size_t, const std::size_t, double *const); -template double combinatoric_rectangular(const std::size_t, const std::size_t, double *const); -template double glynn(const std::size_t, const std::size_t, double *const); -template double glynn_rectangular(const std::size_t, const std::size_t, double *const); -template double ryser(const std::size_t, const std::size_t, double *const); -template double ryser_rectangular(const std::size_t, const std::size_t, double *const); -template double opt(const std::size_t, const std::size_t, double *const); - -template std::complex combinatoric>(const std::size_t, const std::size_t, std::complex *const); -template std::complex combinatoric_rectangular>(const std::size_t, const std::size_t, std::complex *const); -template std::complex glynn>(const std::size_t, const std::size_t, std::complex *const); -template std::complex glynn_rectangular>(const std::size_t, const std::size_t, std::complex *const); -template std::complex ryser>(const std::size_t, const std::size_t, std::complex *const); -template std::complex ryser_rectangular>(const std::size_t, const std::size_t, std::complex *const); -template std::complex opt>(const std::size_t, const std::size_t, std::complex *const); +template std::complex opt>( + const std::size_t, const std::size_t, const std::complex *); diff --git a/src/permanent.h b/src/permanent.h index 9b03337..8e9247c 100644 --- a/src/permanent.h +++ b/src/permanent.h @@ -1,17 +1,15 @@ -#ifndef PERMANENT_PERMANENT_H -#define PERMANENT_PERMANENT_H +/* Copyright 2034 QC-Devs (GPLv3) */ - -#include +#ifndef PERMANENT_H_ +#define PERMANENT_H_ #include +#include -#include "perm-mv0.h" #include "kperm-gray.h" - +#include "perm-mv0.h" #include "tables.h" - #ifdef WITH_TUNING_FILE /* Include tuning file. */ @@ -22,30 +20,33 @@ /* Set default tuning parameters. */ -#define PARAM_1 -0.572098 -#define PARAM_2 -22.014212 -#define PARAM_3 15.297940 -#define PARAM_4 3.0 - -#endif /* WITH_TUNING_FILE */ +constexpr double PARAM_1 = -0.572098; +constexpr double PARAM_2 = -22.014212; +constexpr double PARAM_3 = 15.29794; +constexpr double PARAM_4 = 3.0; +#endif // WITH_TUNING_FILE /* Allow type promotion to complex. */ template -struct identity_t { typedef T type; }; - -#define COMPLEX_OPS(OP) \ - \ - template \ - std::complex<_Tp> \ - operator OP(std::complex<_Tp> lhs, const typename identity_t<_Tp>::type & rhs) \ - { return lhs OP rhs; } \ - \ - template \ - std::complex<_Tp> \ - operator OP(const typename identity_t<_Tp>::type & lhs, const std::complex<_Tp> & rhs) \ - { return lhs OP rhs; } \ +struct identity_t { + typedef T type; +}; + +#define COMPLEX_OPS(OP) \ + \ + template \ + std::complex<_Tp> operator OP( \ + std::complex<_Tp> lhs, const typename identity_t<_Tp>::type & rhs) { \ + return lhs OP rhs; \ + } \ + \ + template \ + std::complex<_Tp> operator OP(const typename identity_t<_Tp>::type & lhs, \ + const std::complex<_Tp> &rhs) { \ + return lhs OP rhs; \ + } COMPLEX_OPS(+) COMPLEX_OPS(-) @@ -54,10 +55,8 @@ COMPLEX_OPS(/) #undef COMPLEX_OPS - -template -T combinatoric(const std::size_t m, const std::size_t n, T *const ptr) -{ +template +T combinatoric(const std::size_t m, const std::size_t n, const T *ptr) { (void)n; perm_mv0 permutations(m); @@ -65,58 +64,43 @@ T combinatoric(const std::size_t m, const std::size_t n, T *const ptr) const std::size_t *perm = permutations.data(); std::size_t i; - T prod; T out = 0.0; do { - - prod = 1.0; - + T prod = 1.0; for (i = 0; i < m; ++i) { prod *= ptr[i * m + perm[i]]; } - out += prod; - - } - while (permutations.next()); + } while (permutations.next()); return out; } - -template -T combinatoric_rectangular(const std::size_t m, const std::size_t n, T *const ptr) -{ +template +T combinatoric_rectangular(const std::size_t m, const std::size_t n, + const T *ptr) { kperm_gray permutations(n); permutations.first(m); const std::size_t *perm = permutations.data(); std::size_t i; - T prod; T out = 0.0; do { - - prod = 1.0; - + T prod = 1.0; for (i = 0; i < m; ++i) { prod *= ptr[i * n + perm[i]]; } - out += prod; - - } - while (permutations.next()); + } while (permutations.next()); return out; } - -template -T glynn(const std::size_t m, const std::size_t n, T *const ptr) -{ +template +T glynn(const std::size_t m, const std::size_t n, const T *ptr) { (void)n; std::size_t i, j; @@ -127,11 +111,12 @@ T glynn(const std::size_t m, const std::size_t n, T *const ptr) int sign = 1; int delta[64]; - T sum, prod; + T sum; T out = 1.0; T vec[64]; - /* Fill delta array (all +1 to start), and permutation array with [0...m]. */ + /* Fill delta array (all +1 to start), and permutation array with [0...m]. + */ for (i = 0; i < m; ++i) { perm[i] = i; @@ -154,7 +139,6 @@ T glynn(const std::size_t m, const std::size_t n, T *const ptr) /* Iterate from the second to the final permutation. */ while (pos != bound) { - /* Update sign and delta. */ sign *= -1; @@ -172,7 +156,7 @@ T glynn(const std::size_t m, const std::size_t n, T *const ptr) /* Multiply by the product of the vectors in delta. */ - prod = 1.0; + T prod = 1.0; for (i = 0; i < m; ++i) { prod *= vec[i]; } @@ -192,10 +176,8 @@ T glynn(const std::size_t m, const std::size_t n, T *const ptr) return out / (1UL << bound); } - -template -T glynn_rectangular(const std::size_t m, const std::size_t n, T *const ptr) -{ +template +T glynn_rectangular(const std::size_t m, const std::size_t n, const T *ptr) { std::size_t i, j, k; std::size_t pos = 0; std::size_t bound = n - 1; @@ -204,11 +186,12 @@ T glynn_rectangular(const std::size_t m, const std::size_t n, T *const ptr) int sign = 1; int delta[64]; - T sum, prod; + T sum; T out = 1.0; T vec[64]; - /* Fill delta array (all +1 to start), and permutation array with [0...n]. */ + /* Fill delta array (all +1 to start), and permutation array with [0...n]. + */ for (i = 0; i < n; ++i) { delta[i] = 1; @@ -234,7 +217,6 @@ T glynn_rectangular(const std::size_t m, const std::size_t n, T *const ptr) /* Iterate from the second to the final permutation. */ while (pos != bound) { - /* Update sign and delta. */ sign *= -1; @@ -256,7 +238,7 @@ T glynn_rectangular(const std::size_t m, const std::size_t n, T *const ptr) /* Multiply by the product of the vectors in delta. */ - prod = 1.0; + T prod = 1.0; for (i = 0; i < n; ++i) { prod *= vec[i]; } @@ -276,34 +258,32 @@ T glynn_rectangular(const std::size_t m, const std::size_t n, T *const ptr) return out / ((1UL << bound) * FACTORIAL(n - m)); } - -template -T ryser(const std::size_t m, const std::size_t n, T *const ptr) -{ +template +T ryser(const std::size_t m, const std::size_t n, const T *ptr) { (void)n; std::size_t i, j; std::size_t k; - T rowsum, rowsumprod; + T rowsum; T out = 0; - /* Iterate over c = pow(2, m) submatrices (equal to (1 << m)) submatrices. */ + /* Iterate over c = pow(2, m) submatrices (equal to (1 << m)) submatrices. + */ std::size_t c = 1UL << m; /* Loop over columns of submatrix; compute product of row sums. */ for (k = 0; k < c; ++k) { - rowsumprod = 1.0; + T rowsumprod = 1.0; for (i = 0; i < m; ++i) { - /* Loop over rows of submatrix; compute row sum. */ rowsum = 0.0; for (j = 0; j < m; ++j) { - - /* Add element to row sum if the row index is in the characteristic * - * vector of the submatrix, which is the binary vector given by k. */ + /* Add element to row sum if the row index is in the + * characteristic * vector of the submatrix, which is the binary + * vector given by k. */ if (k & (1UL << j)) { rowsum += ptr[m * i + j]; @@ -325,9 +305,8 @@ T ryser(const std::size_t m, const std::size_t n, T *const ptr) return out * ((m % 2 == 1) ? -1 : 1); } - // template -// T ryser_rectangular(const std::size_t m, const std::size_t n, T *const ptr) +// T ryser_rectangular(const std::size_t m, const std::size_t n, const T *ptr) // { // kperm_gray permutations(n); // @@ -379,37 +358,34 @@ T ryser(const std::size_t m, const std::size_t n, T *const ptr) // return out; // } - -namespace { - -inline void swap2(std::size_t *perm, std::size_t i, std::size_t j) -{ - const std::size_t temp = perm[i]; +template +void swap2(T *perm, T i, T j) { + const T temp = perm[i]; perm[i] = perm[j]; perm[j] = temp; } - -void init_perm(const std::size_t N, std::size_t *const the_fact_set, std::size_t *const the_perm_set, std::size_t *const the_inv_set) -{ - for (std::size_t i = 0; i < N; i++) { +template +void init_perm(const T N, T *const the_fact_set, T *const the_perm_set, + T *const the_inv_set) { + for (T i = 0; i < N; i++) { the_fact_set[i + 1] = 0; the_perm_set[i] = i; the_inv_set[i] = i; } } - -bool gen_next_perm(std::size_t *const falling_fact, std::size_t *const perm, std::size_t *const inv_perm, const std::size_t cols, const std::size_t u_) -{ +template +bool gen_next_perm(T *const falling_fact, T *const perm, T *const inv_perm, + const T cols, const T u_) { /* Use the current permutation to check for next permutation * lexicographically, if it exists update the curr_array by swapping the * leftmost changed element (at position i < k). Replace the elements up to * position k by the smallest element lying to the right of i. Do not put * remaining k, .., n - 1 positions in ascending order to improve efficiency * has time complexity O(n) in the worst case. */ - std::size_t i = u_ - 1; - std::size_t m1 = cols - i - 1; + T i = u_ - 1; + T m1 = cols - i - 1; /* Begin update of falling_fact - recall falling_fact[0] = 0, so increment * index accordingly. If i becomes negative during the check, you are @@ -428,62 +404,58 @@ bool gen_next_perm(std::size_t *const falling_fact, std::size_t *const perm, std /* Find smallest element perm[i] < perm[j] that lies to the right of * pos i, and then update the state of the permutation using its inverse * to generate next. */ - std::size_t z = (std::size_t)perm[i]; + T z = perm[i]; do { ++z; } while (inv_perm[z] <= i); - const std::size_t j = inv_perm[z]; + const T j = inv_perm[z]; swap2(perm, i, j); swap2(inv_perm, perm[i], perm[j]); ++i; - std::size_t y = 0; + T y = 0; /* Find the smallest elements to the right of position i. */ while (i < u_) { while (inv_perm[y] < i) { ++y; } - const std::size_t j = inv_perm[y]; - swap2(perm, i, j); - swap2(inv_perm, perm[i], perm[j]); + const T k = inv_perm[y]; + swap2(perm, i, k); + swap2(inv_perm, perm[i], perm[k]); ++i; } return true; } -} /* namespace */ - - -template -T ryser_rectangular(const std::size_t m, const std::size_t n, T *const ptr) -{ +template +T ryser_rectangular(const std::size_t m, const std::size_t n, const T *ptr) { std::size_t falling_fact[128]; std::size_t perm[128]; std::size_t inv_perm[128]; falling_fact[0] = 0; - std::size_t i, j, k, counter, sort_up_to; + std::size_t i, j, k; int value_sign = 1; - T bin_c, sum_of_matrix_vals, prod_of_cols, result; + T sum_of_matrix_vals; T sum_over_k_vals = 0.0; T vec[64]; /* Dealing with a rectangle. Can't use bit hacking trick here. */ for (k = 0; k < m; ++k) { /* Store the binomial coefficient for this k value bin_c. */ - bin_c = (double)(BINOMIAL((n - m + k), k)); - counter = 0; // Count how many permutations you have generated - sum_of_matrix_vals = 0.0; - prod_of_cols = 1.0; - result = 0.0; + std::size_t counter = + 0; // Count how many permutations you have generated + T bin_c = BINOMIAL((n - m + k), k); + T prod_of_cols = 1.0; + T result = 0.0; /* (Re)initialize the set to permute for this k value. */ init_perm(n, falling_fact, perm, inv_perm); /* sort up to position u + 1 where u = min(m - k, n - 1). */ - sort_up_to = n - 1; + std::size_t sort_up_to = n - 1; if ((m - k) < sort_up_to) { sort_up_to = (m - k); @@ -495,7 +467,6 @@ T ryser_rectangular(const std::size_t m, const std::size_t n, T *const ptr) sum_of_matrix_vals += ptr[i * n + perm[j]]; } vec[i] = sum_of_matrix_vals; - sum_of_matrix_vals = 0.0; } for (i = 0; i < m; ++i) { prod_of_cols *= vec[i]; @@ -506,14 +477,12 @@ T ryser_rectangular(const std::size_t m, const std::size_t n, T *const ptr) /* Iterate over second to last permutations of the set. */ while (gen_next_perm(falling_fact, perm, inv_perm, n, sort_up_to)) { - sum_of_matrix_vals = 0.0; for (i = 0; i < m; ++i) { sum_of_matrix_vals = 0.0; for (j = 0; j < m - k; ++j) { sum_of_matrix_vals += ptr[i * n + perm[j]]; } vec[i] = sum_of_matrix_vals; - sum_of_matrix_vals = 0.0; } prod_of_cols = 1.0; for (i = 0; i < m; ++i) { @@ -523,26 +492,24 @@ T ryser_rectangular(const std::size_t m, const std::size_t n, T *const ptr) result += value_sign * bin_c * prod_of_cols; counter += 1; } - sum_over_k_vals += (result / (double)counter) * BINOMIAL(n, (m - k)); + sum_over_k_vals += (result / counter) * BINOMIAL(n, (m - k)); value_sign *= -1; } return sum_over_k_vals; } - -template -T opt(const std::size_t m, const std::size_t n, T *const ptr) -{ +template +T opt(const std::size_t m, const std::size_t n, const T *ptr) { /* Use the fastest algorithm. */ if (m == n && n <= PARAM_4) { return ryser(m, n, ptr); - } else if (n * PARAM_1 + ((double)m / n) * PARAM_2 + PARAM_3 > 0) { - return (m == n) ? combinatoric(m, n, ptr) : combinatoric_rectangular(m, n, ptr); + } else if (n * PARAM_1 + PARAM_2 * m / n + PARAM_3 > 0) { + return (m == n) ? combinatoric(m, n, ptr) + : combinatoric_rectangular(m, n, ptr); } else { return (m == n) ? glynn(m, n, ptr) : glynn_rectangular(m, n, ptr); } } - -#endif /* PERMANENT_PERMANENT_H */ +#endif // PERMANENT_H_ diff --git a/src/py_permanent.cc b/src/py_permanent.cc index 2b13dcf..eaeb223 100644 --- a/src/py_permanent.cc +++ b/src/py_permanent.cc @@ -1,7 +1,4 @@ -#include -#include - -#include +/* Copyright 2034 QC-Devs (GPLv3) */ #include @@ -9,15 +6,17 @@ #include #include -#include "permanent.h" +#include +#include +#include +#include "permanent.h" static const char DOCSTRING_MODULE[] = R"""( Permanent module C extension. )"""; - static const char DOCSTRING_PERMANENT[] = R"""( Compute the permanent of a matrix using the best algorithm for the shape of the given matrix. @@ -32,7 +31,6 @@ permanent : (np.double|np.complex) )"""; - static const char DOCSTRING_COMBINATORIC[] = R"""( Compute the permanent of a matrix combinatorically. @@ -53,7 +51,6 @@ permanent : (np.double|np.complex) )"""; - static const char DOCSTRING_GLYNN[] = R"""( Compute the permanent of a matrix via Glynn's algorithm [Glynn]_. @@ -112,7 +109,6 @@ permanent : (np.double|np.complex) )"""; - static const char DOCSTRING_RYSER[] = R"""( Compute the permanent of a matrix via Ryser's algorithm [Ryser]_. @@ -142,14 +138,14 @@ permanent : (np.double|np.complex) )"""; - -static PyObject *py_opt(PyObject *module, PyObject *object) -{ +static PyObject *py_opt(PyObject *module, PyObject *object) { (void)module; - PyArrayObject *matrix = (PyArrayObject *)PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr); + PyArrayObject *matrix = reinterpret_cast( + PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr)); if (!matrix) { - PyErr_SetString(PyExc_TypeError, "Argument must be a 2-dimensional array"); + PyErr_SetString(PyExc_TypeError, + "Argument must be a 2-dimensional array"); return nullptr; } @@ -158,33 +154,36 @@ static PyObject *py_opt(PyObject *module, PyObject *object) if (m > n) { return py_opt(module, PyArray_Transpose(matrix, nullptr)); } else if (m > 64 || n > 64) { - PyErr_SetString(PyExc_ValueError, "Argument array must have <=64 rows and columns"); + PyErr_SetString(PyExc_ValueError, + "Argument array must have <=64 rows and columns"); return nullptr; } int type = PyArray_TYPE(matrix); if (type == NPY_DOUBLE) { - double *ptr = (double *)PyArray_GETPTR2(matrix, 0, 0); + const double *ptr = + reinterpret_cast(PyArray_GETPTR2(matrix, 0, 0)); return PyFloat_FromDouble(opt(m, n, ptr)); - } - else if (type == NPY_COMPLEX128) { - std::complex *ptr = (std::complex *)PyArray_GETPTR2(matrix, 0, 0); + } else if (type == NPY_COMPLEX128) { + std::complex *ptr = reinterpret_cast *>( + PyArray_GETPTR2(matrix, 0, 0)); std::complex out = opt>(m, n, ptr); return PyComplex_FromDoubles(out.real(), out.imag()); } else { - PyErr_SetString(PyExc_TypeError, "Array must have dtype (double|complex)"); + PyErr_SetString(PyExc_TypeError, + "Array must have dtype (double|complex)"); return nullptr; } } - -static PyObject *py_combinatoric(PyObject *module, PyObject *object) -{ +static PyObject *py_combinatoric(PyObject *module, PyObject *object) { (void)module; - PyArrayObject *matrix = (PyArrayObject *)PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr); + PyArrayObject *matrix = reinterpret_cast( + PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr)); if (!matrix) { - PyErr_SetString(PyExc_TypeError, "Argument must be a 2-dimensional array"); + PyErr_SetString(PyExc_TypeError, + "Argument must be a 2-dimensional array"); return nullptr; } @@ -196,32 +195,33 @@ static PyObject *py_combinatoric(PyObject *module, PyObject *object) int type = PyArray_TYPE(matrix); if (type == NPY_DOUBLE) { - double *ptr = (double *)PyArray_GETPTR2(matrix, 0, 0); + double *ptr = reinterpret_cast(PyArray_GETPTR2(matrix, 0, 0)); return PyFloat_FromDouble( (m == n) ? combinatoric(m, n, ptr) - : combinatoric_rectangular(m, n, ptr) - ); - } - else if (type == NPY_COMPLEX128) { - std::complex *ptr = (std::complex *)PyArray_GETPTR2(matrix, 0, 0); + : combinatoric_rectangular(m, n, ptr)); + } else if (type == NPY_COMPLEX128) { + std::complex *ptr = reinterpret_cast *>( + PyArray_GETPTR2(matrix, 0, 0)); std::complex out = - (m == n) ? combinatoric>(m, n, ptr) - : combinatoric_rectangular>(m, n, ptr); + (m == n) + ? combinatoric>(m, n, ptr) + : combinatoric_rectangular>(m, n, ptr); return PyComplex_FromDoubles(out.real(), out.imag()); } else { - PyErr_SetString(PyExc_TypeError, "Array must have dtype (double|complex)"); + PyErr_SetString(PyExc_TypeError, + "Array must have dtype (double|complex)"); return nullptr; } } - -static PyObject *py_glynn(PyObject *module, PyObject *object) -{ +static PyObject *py_glynn(PyObject *module, PyObject *object) { (void)module; - PyArrayObject *matrix = (PyArrayObject *)PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr); + PyArrayObject *matrix = reinterpret_cast( + PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr)); if (!matrix) { - PyErr_SetString(PyExc_TypeError, "Argument must be a 2-dimensional array"); + PyErr_SetString(PyExc_TypeError, + "Argument must be a 2-dimensional array"); return nullptr; } @@ -233,32 +233,32 @@ static PyObject *py_glynn(PyObject *module, PyObject *object) int type = PyArray_TYPE(matrix); if (type == NPY_DOUBLE) { - double *ptr = (double *)PyArray_GETPTR2(matrix, 0, 0); - return PyFloat_FromDouble( - (m == n) ? glynn(m, n, ptr) - : glynn_rectangular(m, n, ptr) - ); - } - else if (type == NPY_COMPLEX128) { - std::complex *ptr = (std::complex *)PyArray_GETPTR2(matrix, 0, 0); + double *ptr = reinterpret_cast(PyArray_GETPTR2(matrix, 0, 0)); + return PyFloat_FromDouble((m == n) + ? glynn(m, n, ptr) + : glynn_rectangular(m, n, ptr)); + } else if (type == NPY_COMPLEX128) { + std::complex *ptr = reinterpret_cast *>( + PyArray_GETPTR2(matrix, 0, 0)); std::complex out = (m == n) ? glynn>(m, n, ptr) : glynn_rectangular>(m, n, ptr); return PyComplex_FromDoubles(out.real(), out.imag()); } else { - PyErr_SetString(PyExc_TypeError, "Array must have dtype (double|complex)"); + PyErr_SetString(PyExc_TypeError, + "Array must have dtype (double|complex)"); return nullptr; } } - -static PyObject *py_ryser(PyObject *module, PyObject *object) -{ +static PyObject *py_ryser(PyObject *module, PyObject *object) { (void)module; - PyArrayObject *matrix = (PyArrayObject *)PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr); + PyArrayObject *matrix = reinterpret_cast( + PyArray_FromAny(object, nullptr, 2, 2, NPY_ARRAY_CARRAY_RO, nullptr)); if (!matrix) { - PyErr_SetString(PyExc_TypeError, "Argument must be a 2-dimensional array"); + PyErr_SetString(PyExc_TypeError, + "Argument must be a 2-dimensional array"); return nullptr; } @@ -270,25 +270,24 @@ static PyObject *py_ryser(PyObject *module, PyObject *object) int type = PyArray_TYPE(matrix); if (type == NPY_DOUBLE) { - double *ptr = (double *)PyArray_GETPTR2(matrix, 0, 0); - return PyFloat_FromDouble( - (m == n) ? ryser(m, n, ptr) - : ryser_rectangular(m, n, ptr) - ); - } - else if (type == NPY_COMPLEX128) { - std::complex *ptr = (std::complex *)PyArray_GETPTR2(matrix, 0, 0); + double *ptr = reinterpret_cast(PyArray_GETPTR2(matrix, 0, 0)); + return PyFloat_FromDouble((m == n) + ? ryser(m, n, ptr) + : ryser_rectangular(m, n, ptr)); + } else if (type == NPY_COMPLEX128) { + std::complex *ptr = reinterpret_cast *>( + PyArray_GETPTR2(matrix, 0, 0)); std::complex out = (m == n) ? ryser>(m, n, ptr) : ryser_rectangular>(m, n, ptr); return PyComplex_FromDoubles(out.real(), out.imag()); } else { - PyErr_SetString(PyExc_TypeError, "Array must have dtype (double|complex)"); + PyErr_SetString(PyExc_TypeError, + "Array must have dtype (double|complex)"); return nullptr; } } - /* Define the Python methods that will go into the C extension module. * * * * Note: METH_O indicates that the Python function takes a single argument. * @@ -298,25 +297,32 @@ static PyObject *py_ryser(PyObject *module, PyObject *object) static PyMethodDef methods[] = { /* Python function name C function Args flag Docstring */ - { "opt", py_opt, METH_O, DOCSTRING_PERMANENT }, - { "combinatoric", py_combinatoric, METH_O, DOCSTRING_COMBINATORIC }, - { "glynn", py_glynn, METH_O, DOCSTRING_GLYNN }, - { "ryser", py_ryser, METH_O, DOCSTRING_RYSER }, - { nullptr, nullptr, 0, nullptr } /* sentinel value */ + {"opt", py_opt, METH_O, DOCSTRING_PERMANENT}, + {"combinatoric", py_combinatoric, METH_O, DOCSTRING_COMBINATORIC}, + {"glynn", py_glynn, METH_O, DOCSTRING_GLYNN}, + {"ryser", py_ryser, METH_O, DOCSTRING_RYSER}, + {nullptr, nullptr, 0, nullptr} /* sentinel value */ }; - /* Define the C extension module. */ static struct PyModuleDef definition = { - PyModuleDef_HEAD_INIT, "permanent", DOCSTRING_MODULE, -1, methods, nullptr, nullptr, nullptr, nullptr + PyModuleDef_HEAD_INIT, + "permanent", + DOCSTRING_MODULE, + -1, + methods, + nullptr, + nullptr, + nullptr, + nullptr, }; - /* Initialize the C extension module. */ +// cppcheck-suppress unusedFunction PyMODINIT_FUNC PyInit_permanent(void) { - Py_Initialize(); /* Initialize Python API */ - import_array(); /* Initialize NumPy NDArray API */ - return PyModule_Create(&definition); /* Create module. */ + Py_Initialize(); /* Initialize Python API */ + import_array(); /* Initialize NumPy NDArray API */ + return PyModule_Create(&definition); /* Create module. */ } diff --git a/src/tables.h b/src/tables.h index b61596b..d779861 100644 --- a/src/tables.h +++ b/src/tables.h @@ -1,16 +1,14 @@ -#ifndef PERMANENT_BINOMIAL_H -#define PERMANENT_BINOMIAL_H +/* Copyright 2034 QC-Devs (GPLv3) */ +#ifndef TABLES_H_ +#define TABLES_H_ #include - #define BINOMIAL(N, K) binom_table[65 * N + K] - #define FACTORIAL(N) factorial_table[N] - static const std::size_t binom_table[] = { 1, 0, @@ -4236,77 +4234,32 @@ static const std::size_t binom_table[] = { 41664, 2016, 64, - 1 + 1, }; - static const double factorial_table[] = { - 1.0000000000000000e+00, - 1.0000000000000000e+00, - 2.0000000000000000e+00, - 6.0000000000000000e+00, - 2.4000000000000000e+01, - 1.2000000000000000e+02, - 7.2000000000000000e+02, - 5.0400000000000000e+03, - 4.0320000000000000e+04, - 3.6288000000000000e+05, - 3.6288000000000000e+06, - 3.9916800000000000e+07, - 4.7900160000000000e+08, - 6.2270208000000000e+09, - 8.7178291200000000e+10, - 1.3076743680000000e+12, - 2.0922789888000000e+13, - 3.5568742809600000e+14, - 6.4023737057280000e+15, - 1.2164510040883200e+17, - 2.4329020081766400e+18, - 5.1090942171709440e+19, - 1.1240007277776077e+21, - 2.5852016738884978e+22, - 6.2044840173323941e+23, - 1.5511210043330984e+25, - 4.0329146112660572e+26, - 1.0888869450418352e+28, - 3.0488834461171380e+29, - 8.8417619937397008e+30, - 2.6525285981219110e+32, - 8.2228386541779236e+33, - 2.6313083693369355e+35, - 8.6833176188118871e+36, - 2.9523279903960420e+38, - 1.0333147966386147e+40, - 3.7199332678990118e+41, - 1.3763753091226346e+43, - 5.2302261746660112e+44, - 2.0397882081197442e+46, - 8.1591528324789801e+47, - 3.3452526613163818e+49, - 1.4050061177528803e+51, - 6.0415263063373834e+52, - 2.6582715747884485e+54, - 1.1962222086548019e+56, - 5.5026221598120899e+57, - 2.5862324151116818e+59, - 1.2413915592536073e+61, - 6.0828186403426752e+62, - 3.0414093201713381e+64, - 1.5511187532873824e+66, - 8.0658175170943877e+67, - 4.2748832840600255e+69, - 2.3084369733924138e+71, - 1.2696403353658278e+73, - 7.1099858780486358e+74, - 4.0526919504877214e+76, - 2.3505613312828785e+78, - 1.3868311854568984e+80, - 8.3209871127413899e+81, - 5.0758021387722484e+83, - 3.1469973260387939e+85, - 1.9826083154044403e+87, - 1.2688693218588415e+89, + 1.0000000000000000e+00, 1.0000000000000000e+00, 2.0000000000000000e+00, + 6.0000000000000000e+00, 2.4000000000000000e+01, 1.2000000000000000e+02, + 7.2000000000000000e+02, 5.0400000000000000e+03, 4.0320000000000000e+04, + 3.6288000000000000e+05, 3.6288000000000000e+06, 3.9916800000000000e+07, + 4.7900160000000000e+08, 6.2270208000000000e+09, 8.7178291200000000e+10, + 1.3076743680000000e+12, 2.0922789888000000e+13, 3.5568742809600000e+14, + 6.4023737057280000e+15, 1.2164510040883200e+17, 2.4329020081766400e+18, + 5.1090942171709440e+19, 1.1240007277776077e+21, 2.5852016738884978e+22, + 6.2044840173323941e+23, 1.5511210043330984e+25, 4.0329146112660572e+26, + 1.0888869450418352e+28, 3.0488834461171380e+29, 8.8417619937397008e+30, + 2.6525285981219110e+32, 8.2228386541779236e+33, 2.6313083693369355e+35, + 8.6833176188118871e+36, 2.9523279903960420e+38, 1.0333147966386147e+40, + 3.7199332678990118e+41, 1.3763753091226346e+43, 5.2302261746660112e+44, + 2.0397882081197442e+46, 8.1591528324789801e+47, 3.3452526613163818e+49, + 1.4050061177528803e+51, 6.0415263063373834e+52, 2.6582715747884485e+54, + 1.1962222086548019e+56, 5.5026221598120899e+57, 2.5862324151116818e+59, + 1.2413915592536073e+61, 6.0828186403426752e+62, 3.0414093201713381e+64, + 1.5511187532873824e+66, 8.0658175170943877e+67, 4.2748832840600255e+69, + 2.3084369733924138e+71, 1.2696403353658278e+73, 7.1099858780486358e+74, + 4.0526919504877214e+76, 2.3505613312828785e+78, 1.3868311854568984e+80, + 8.3209871127413899e+81, 5.0758021387722484e+83, 3.1469973260387939e+85, + 1.9826083154044403e+87, 1.2688693218588415e+89, }; - -#endif /* PERMANENT_BINOMIAL_H */ +#endif // TABLES_H_ diff --git a/src/tuning.cc b/src/tuning.cc index 1b72cb0..6c934d1 100644 --- a/src/tuning.cc +++ b/src/tuning.cc @@ -1,15 +1,20 @@ -#include +/* Copyright 2034 QC-Devs (GPLv3) */ + #include +#include #include - #include -#include #include +#include #include #include "permanent.h" +#ifdef WITH_TUNING_FILE +#include "tuning.h" + +#endif // WITH_TUNING_FILE #ifdef RUN_TUNING @@ -39,14 +44,11 @@ constexpr double DEFAULT_PARAM_3 = 15.297940; constexpr double DEFAULT_PARAM_4 = 3.0; -#endif /* RUN_TUNING */ - - +#endif // RUN_TUNING #ifdef RUN_TUNING -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { (void)argc; (void)argv; @@ -56,8 +58,7 @@ int main(int argc, char *argv[]) csv_file.open(CSV_FILE); - if (csv_file.fail()) - { + if (csv_file.fail()) { std::cerr << "Cannot open CSV file" << std::endl; return 2; } @@ -78,8 +79,7 @@ int main(int argc, char *argv[]) csv_file << CSV_HEADER << '\n'; - if (csv_file.fail()) - { + if (csv_file.fail()) { std::cerr << "Error writing to CSV file" << std::endl; csv_file.close(); return 2; @@ -89,95 +89,87 @@ int main(int argc, char *argv[]) std::size_t i, m, n; - double soln_combn; - double soln_glynn; - double soln_ryser; - std::clock_t begin, end; + double soln_combn, soln_glynn, soln_ryser; + double mean_combn, mean_glynn, mean_ryser; + double time_combn[128]; double time_glynn[128]; double time_ryser[128]; - double mean_combn, mean_glynn, mean_ryser; + double array[MAX_ROWS * MAX_COLS]; int fastest; - double array[MAX_ROWS * MAX_COLS]; - /* Random binary matrix for testing. */ std::srand(1234789789U); - for (i = 0; i < MAX_ROWS * MAX_COLS; ++i) - { - array[i] = (std::rand() / (double)RAND_MAX - 0.5) * 2; + for (i = 0; i < MAX_ROWS * MAX_COLS; ++i) { + array[i] = (std::rand() / static_cast(RAND_MAX) - 0.5) * 2; } /* Iterate over number of rows and number of columns. */ - for (m = 2; m <= MAX_ROWS; ++m) - { - for (n = m; n <= MAX_COLS; ++n) - { - /* Solve the permanent using each algorithm NUM_TRIALS number of times. */ - - if (m == n) - { - for (i = 0; i != NUM_TRIALS; ++i) - { + for (m = 2; m <= MAX_ROWS; ++m) { + for (n = m; n <= MAX_COLS; ++n) { + /* Solve the permanent using each algorithm NUM_TRIALS number of + * times. */ + if (m == n) { + for (i = 0; i != NUM_TRIALS; ++i) { begin = std::clock(); soln_combn = combinatoric(m, n, array); end = std::clock(); - time_combn[i] = (double)(end - begin); + time_combn[i] = static_cast(end - begin); begin = std::clock(); soln_glynn = glynn(m, n, array); end = std::clock(); - time_glynn[i] = (double)(end - begin); + time_glynn[i] = static_cast(end - begin); begin = std::clock(); soln_ryser = ryser(m, n, array); end = std::clock(); - time_ryser[i] = (double)(end - begin); + time_ryser[i] = static_cast(end - begin); - if ((std::fabs(soln_combn - soln_glynn) / soln_combn > TOLERANCE) || - (std::fabs(soln_combn - soln_ryser) / soln_ryser > TOLERANCE)) { + if ((std::fabs(soln_combn - soln_glynn) / soln_combn > + TOLERANCE) || + (std::fabs(soln_combn - soln_ryser) / soln_ryser > + TOLERANCE)) { std::cerr << "Bad permanent values:" - << "\nCombn: " << soln_combn - << "\nGlynn: " << soln_glynn - << "\nRyser: " << soln_ryser << std::endl; + << "\nCombn: " << soln_combn + << "\nGlynn: " << soln_glynn + << "\nRyser: " << soln_ryser << std::endl; csv_file.close(); return 1; } } - } - else - { - for (i = 0; i != NUM_TRIALS; ++i) - { + } else { + for (i = 0; i != NUM_TRIALS; ++i) { begin = std::clock(); soln_combn = combinatoric_rectangular(m, n, array); end = std::clock(); - time_combn[i] = (double)(end - begin); + time_combn[i] = static_cast(end - begin); begin = std::clock(); soln_glynn = glynn_rectangular(m, n, array); end = std::clock(); - time_glynn[i] = (double)(end - begin); + time_glynn[i] = static_cast(end - begin); begin = std::clock(); soln_ryser = ryser_rectangular(m, n, array); end = std::clock(); - time_ryser[i] = (double)(end - begin); - - if ((std::fabs(soln_combn - soln_glynn) / soln_combn > TOLERANCE) || - (std::fabs(soln_combn - soln_ryser) / soln_ryser > TOLERANCE)) { - std::cerr << std::scientific - << "Bad permanent values:" - << "\nCombn: " << soln_combn - << "\nGlynn: " << soln_glynn - << "\nRyser: " << soln_ryser << std::endl; + time_ryser[i] = static_cast(end - begin); + + if ((std::fabs(soln_combn - soln_glynn) / soln_combn > + TOLERANCE) || + (std::fabs(soln_combn - soln_ryser) / soln_ryser > + TOLERANCE)) { + std::cerr << std::scientific << "Bad permanent values:" + << "\nCombn: " << soln_combn + << "\nGlynn: " << soln_glynn + << "\nRyser: " << soln_ryser << std::endl; csv_file.close(); return 1; } @@ -190,16 +182,15 @@ int main(int argc, char *argv[]) mean_glynn = 0.0; mean_ryser = 0.0; - for (i = 0; i != NUM_TRIALS; ++i) - { + for (i = 0; i != NUM_TRIALS; ++i) { mean_combn += time_combn[i]; mean_glynn += time_glynn[i]; mean_ryser += time_ryser[i]; } - mean_combn = (double)mean_combn / (double)NUM_TRIALS; - mean_glynn = (double)mean_glynn / (double)NUM_TRIALS; - mean_ryser = (double)mean_ryser / (double)NUM_TRIALS; + mean_combn = mean_combn / NUM_TRIALS; + mean_glynn = mean_glynn / NUM_TRIALS; + mean_ryser = mean_ryser / NUM_TRIALS; /* Find the fastest algorithm */ @@ -213,17 +204,18 @@ int main(int argc, char *argv[]) /* Write line */ - std::cout << std::scientific << (double)m / n << ',' << std::setw(2) << n << ',' - << std::scientific << mean_combn << ',' << mean_glynn << ',' - << std::scientific << mean_ryser << ',' << fastest << std::endl; - + std::cout << std::scientific << static_cast(m) / n << ',' + << std::setw(2) << n << ',' << std::scientific + << mean_combn << ',' << mean_glynn << ',' + << std::scientific << mean_ryser << ',' << fastest + << std::endl; - csv_file << std::scientific << (double)m / n << ',' << std::setw(2) << n << ',' - << std::scientific << mean_combn << ',' << mean_glynn << ',' + csv_file << std::scientific << static_cast(m) / n << ',' + << std::setw(2) << n << ',' << std::scientific + << mean_combn << ',' << mean_glynn << ',' << std::scientific << mean_ryser << ',' << fastest << '\n'; - if (csv_file.fail()) - { + if (csv_file.fail()) { std::cerr << "Error writing to CSV file" << std::endl; csv_file.close(); return 2; @@ -240,12 +232,9 @@ int main(int argc, char *argv[]) return 0; } - #else - -int main(int argc, char *argv[]) -{ +int main(int argc, char *argv[]) { (void)argc; (void)argv; @@ -255,8 +244,7 @@ int main(int argc, char *argv[]) header_file.open(HEADER_FILE); - if (header_file.fail()) - { + if (header_file.fail()) { std::cerr << "Cannot open header file" << std::endl; return 2; } @@ -267,15 +255,19 @@ int main(int argc, char *argv[]) /* Write default header file */ - header_file << "#ifndef PERMANENT_TUNING_H\n"; - header_file << "#define PERMANENT_TUNING_H\n"; + header_file << "#ifndef TUNING_H_\n"; + header_file << "#define TUNING_H_\n"; header_file << "\n\n"; - header_file << "#define PARAM_1 " << std::scientific << DEFAULT_PARAM_1 << '\n'; - header_file << "#define PARAM_2 " << std::scientific << DEFAULT_PARAM_2 << '\n'; - header_file << "#define PARAM_3 " << std::scientific << DEFAULT_PARAM_3 << '\n'; - header_file << "#define PARAM_4 " << std::scientific << DEFAULT_PARAM_4 << '\n'; + header_file << "constexpr double PARAM_1 = " << std::scientific + << DEFAULT_PARAM_1 << ";\n"; + header_file << "constexpr double PARAM_2 = " << std::scientific + << DEFAULT_PARAM_2 << ";\n"; + header_file << "constexpr double PARAM_3 = " << std::scientific + << DEFAULT_PARAM_3 << ";\n"; + header_file << "constexpr double PARAM_4 = " << std::scientific + << DEFAULT_PARAM_4 << ";\n"; header_file << "\n\n"; - header_file << "#endif /* PERMANENT_TUNING_H */\n"; + header_file << "#endif // TUNING_H_\n"; if (header_file.fail()) { std::cerr << "Error writing to header file" << std::endl; @@ -292,5 +284,4 @@ int main(int argc, char *argv[]) return 0; } - -#endif /* RUN_TUNING */ +#endif // RUN_TUNING diff --git a/tools/tuning.py b/tools/tuning.py index 5d66388..29fda64 100644 --- a/tools/tuning.py +++ b/tools/tuning.py @@ -79,17 +79,17 @@ try: with open(HEADER_FILE, "w") as file_ptr: file_ptr.write( - f"""#ifndef PERMANENT_TUNING_H -#define PERMANENT_TUNING_H + f"""#ifndef SRC_TUNING_H_ +#define TUNING_H_ -#define PARAM_1 {param_1:.9f} -#define PARAM_2 {param_2:.9f} -#define PARAM_3 {param_3:.9f} -#define PARAM_4 {param_4:.9f} + constexpr double PARAM_1 = {param_1:.9f}; + constexpr double PARAM_2 = {param_2:.9f}; + constexpr double PARAM_3 = {param_3:.9f}; + constexpr double PARAM_4 = {param_4:.9f}; -#endif /* PERMANENT_TUNING_H */ +#endif // TUNING_H_ """ )