TheAlgorithms/C++ 1.0.0
All the algorithms implemented in C++
Loading...
Searching...
No Matches
durand_kerner_roots.cpp
Go to the documentation of this file.
1
32#include <algorithm>
33#include <cassert>
34#include <cmath>
35#include <complex>
36#include <cstdint>
37#include <cstdlib>
38#include <ctime>
39#include <fstream>
40#include <iostream>
41#include <valarray>
42#ifdef _OPENMP
43#include <omp.h>
44#endif
45
46#define ACCURACY 1e-10
54std::complex<double> poly_function(const std::valarray<double> &coeffs,
55 std::complex<double> x) {
56 double real = 0.f, imag = 0.f;
57 int n;
58
59 // #ifdef _OPENMP
60 // #pragma omp target teams distribute reduction(+ : real, imag)
61 // #endif
62 for (n = 0; n < coeffs.size(); n++) {
63 std::complex<double> tmp =
64 coeffs[n] * std::pow(x, coeffs.size() - n - 1);
65 real += tmp.real();
66 imag += tmp.imag();
67 }
68
69 return std::complex<double>(real, imag);
70}
71
77const char *complex_str(const std::complex<double> &x) {
78#define MAX_BUFF_SIZE 50
79 static char msg[MAX_BUFF_SIZE];
80
81 std::snprintf(msg, MAX_BUFF_SIZE, "% 7.04g%+7.04gj", x.real(), x.imag());
82
83 return msg;
84}
85
92bool check_termination(long double delta) {
93 static long double past_delta = INFINITY;
94 if (std::abs(past_delta - delta) <= ACCURACY || delta < ACCURACY)
95 return true;
96 past_delta = delta;
97 return false;
98}
99
110std::pair<uint32_t, double> durand_kerner_algo(
111 const std::valarray<double> &coeffs,
112 std::valarray<std::complex<double>> *roots, bool write_log = false) {
113 long double tol_condition = 1;
114 uint32_t iter = 0;
115 int n;
116 std::ofstream log_file;
117
118 if (write_log) {
119 /*
120 * store intermediate values to a CSV file
121 */
122 log_file.open("durand_kerner.log.csv");
123 if (!log_file.is_open()) {
124 perror("Unable to create a storage log file!");
125 std::exit(EXIT_FAILURE);
126 }
127 log_file << "iter#,";
128
129 for (n = 0; n < roots->size(); n++) log_file << "root_" << n << ",";
130
131 log_file << "avg. correction";
132 log_file << "\n0,";
133 for (n = 0; n < roots->size(); n++)
134 log_file << complex_str((*roots)[n]) << ",";
135 }
136
137 bool break_loop = false;
138 while (!check_termination(tol_condition) && iter < INT16_MAX &&
139 !break_loop) {
140 tol_condition = 0;
141 iter++;
142 break_loop = false;
143
144 if (log_file.is_open())
145 log_file << "\n" << iter << ",";
146
147#ifdef _OPENMP
148#pragma omp parallel for shared(break_loop, tol_condition)
149#endif
150 for (n = 0; n < roots->size(); n++) {
151 if (break_loop)
152 continue;
153
154 std::complex<double> numerator, denominator;
155 numerator = poly_function(coeffs, (*roots)[n]);
156 denominator = 1.0;
157 for (int i = 0; i < roots->size(); i++)
158 if (i != n)
159 denominator *= (*roots)[n] - (*roots)[i];
160
161 std::complex<long double> delta = numerator / denominator;
162
163 if (std::isnan(std::abs(delta)) || std::isinf(std::abs(delta))) {
164 std::cerr << "\n\nOverflow/underrun error - got value = "
165 << std::abs(delta) << "\n";
166 // return std::pair<uint32_t, double>(iter, tol_condition);
167 break_loop = true;
168 }
169
170 (*roots)[n] -= delta;
171
172#ifdef _OPENMP
173#pragma omp critical
174#endif
175 tol_condition = std::max(tol_condition, std::abs(std::abs(delta)));
176 }
177 // tol_condition /= (degree - 1);
178
179 if (break_loop)
180 break;
181
182 if (log_file.is_open()) {
183 for (n = 0; n < roots->size(); n++)
184 log_file << complex_str((*roots)[n]) << ",";
185 }
186
187#if defined(DEBUG) || !defined(NDEBUG)
188 if (iter % 500 == 0) {
189 std::cout << "Iter: " << iter << "\t";
190 for (n = 0; n < roots->size(); n++)
191 std::cout << "\t" << complex_str((*roots)[n]);
192 std::cout << "\t\tabsolute average change: " << tol_condition
193 << "\n";
194 }
195#endif
196
197 if (log_file.is_open())
198 log_file << tol_condition;
199 }
200
201 return std::pair<uint32_t, long double>(iter, tol_condition);
202}
203
208void test1() {
209 const std::valarray<double> coeffs = {1, 0, 4}; // x^2 - 2 = 0
210 std::valarray<std::complex<double>> roots(2);
211 std::valarray<std::complex<double>> expected = {
212 std::complex<double>(0., 2.),
213 std::complex<double>(0., -2.) // known expected roots
214 };
215
216 /* initialize root approximations with random values */
217 for (int n = 0; n < roots.size(); n++) {
218 roots[n] = std::complex<double>(std::rand() % 100, std::rand() % 100);
219 roots[n] -= 50.f;
220 roots[n] /= 25.f;
221 }
222
223 auto result = durand_kerner_algo(coeffs, &roots, false);
224
225 for (int i = 0; i < roots.size(); i++) {
226 // check if approximations are have < 0.1% error with one of the
227 // expected roots
228 bool err1 = false;
229 for (int j = 0; j < roots.size(); j++)
230 err1 |= std::abs(std::abs(roots[i] - expected[j])) < 1e-3;
231 assert(err1);
232 }
233
234 std::cout << "Test 1 passed! - " << result.first << " iterations, "
235 << result.second << " accuracy"
236 << "\n";
237}
238
243void test2() {
244 const std::valarray<double> coeffs = {// 0.015625 x^3 - 1 = 0
245 1. / 64., 0., 0., -1.};
246 std::valarray<std::complex<double>> roots(3);
247 const std::valarray<std::complex<double>> expected = {
248 std::complex<double>(4., 0.), std::complex<double>(-2., 3.46410162),
249 std::complex<double>(-2., -3.46410162) // known expected roots
250 };
251
252 /* initialize root approximations with random values */
253 for (int n = 0; n < roots.size(); n++) {
254 roots[n] = std::complex<double>(std::rand() % 100, std::rand() % 100);
255 roots[n] -= 50.f;
256 roots[n] /= 25.f;
257 }
258
259 auto result = durand_kerner_algo(coeffs, &roots, false);
260
261 for (int i = 0; i < roots.size(); i++) {
262 // check if approximations are have < 0.1% error with one of the
263 // expected roots
264 bool err1 = false;
265 for (int j = 0; j < roots.size(); j++)
266 err1 |= std::abs(std::abs(roots[i] - expected[j])) < 1e-3;
267 assert(err1);
268 }
269
270 std::cout << "Test 2 passed! - " << result.first << " iterations, "
271 << result.second << " accuracy"
272 << "\n";
273}
274
275/***
276 * Main function.
277 * The comandline input arguments are taken as coeffiecients of a
278 *polynomial. For example, this command
279 * ```sh
280 * ./durand_kerner_roots 1 0 -4
281 * ```
282 * will find roots of the polynomial \f$1\cdot x^2 + 0\cdot x^1 + (-4)=0\f$
283 **/
284int main(int argc, char **argv) {
285 /* initialize random seed: */
286 std::srand(std::time(nullptr));
287
288 if (argc < 2) {
289 test1(); // run tests when no input is provided
290 test2(); // and skip tests when input polynomial is provided
291 std::cout << "Please pass the coefficients of the polynomial as "
292 "commandline "
293 "arguments.\n";
294 return 0;
295 }
296
297 int n, degree = argc - 1; // detected polynomial degree
298 std::valarray<double> coeffs(degree); // create coefficiencts array
299
300 // number of roots = degree - 1
301 std::valarray<std::complex<double>> s0(degree - 1);
302
303 std::cout << "Computing the roots for:\n\t";
304 for (n = 0; n < degree; n++) {
305 coeffs[n] = strtod(argv[n + 1], nullptr);
306 if (n < degree - 1 && coeffs[n] != 0)
307 std::cout << "(" << coeffs[n] << ") x^" << degree - n - 1 << " + ";
308 else if (coeffs[n] != 0)
309 std::cout << "(" << coeffs[n] << ") x^" << degree - n - 1
310 << " = 0\n";
311
312 /* initialize root approximations with random values */
313 if (n < degree - 1) {
314 s0[n] = std::complex<double>(std::rand() % 100, std::rand() % 100);
315 s0[n] -= 50.f;
316 s0[n] /= 50.f;
317 }
318 }
319
320 // numerical errors less when the first coefficient is "1"
321 // hence, we normalize the first coefficient
322 {
323 double tmp = coeffs[0];
324 coeffs /= tmp;
325 }
326
327 clock_t end_time, start_time = clock();
328 auto result = durand_kerner_algo(coeffs, &s0, true);
329 end_time = clock();
330
331 std::cout << "\nIterations: " << result.first << "\n";
332 for (n = 0; n < degree - 1; n++)
333 std::cout << "\t" << complex_str(s0[n]) << "\n";
334 std::cout << "absolute average change: " << result.second << "\n";
335 std::cout << "Time taken: "
336 << static_cast<double>(end_time - start_time) / CLOCKS_PER_SEC
337 << " sec\n";
338
339 return 0;
340}
bool check_termination(long double delta)
void test2()
void test1()
std::pair< uint32_t, double > durand_kerner_algo(const std::valarray< double > &coeffs, std::valarray< std::complex< double > > *roots, bool write_log=false)
const char * complex_str(const std::complex< double > &x)
std::complex< double > poly_function(const std::valarray< double > &coeffs, std::complex< double > x)
#define ACCURACY
uint64_t result(uint64_t n)
int main()
Main function.