void write_matrix_to_octave_file( orthotope<T> const& A , std::string const& name ) { BOOST_ASSERT(2 == A.order()); std::size_t const rows = A.extent(0); std::size_t const cols = A.extent(1); std::ofstream file(name + ".mat"); BOOST_ASSERT(file.is_open()); file << "# name: " << name << "\n" << "# type: matrix\n" << "# rows: " << rows << "\n" << "# columns: " << cols << "\n"; for (std::size_t i = 0; i < rows; ++i) { for (std::size_t j = 0; j < cols; ++j) { T const v = (compare_floating(0.0, A(i, j), 1e-6) ? 0.0 : A(i, j)); file << " " << v; } file << "\n"; } file.close(); }
inline void random_symmetric_matrix( orthotope<T>& A , std::size_t seed ) { BOOST_ASSERT(2 == A.order()); BOOST_ASSERT(A.hypercube()); std::size_t const n = A.extent(0); boost::random::mt19937_64 rng(seed); boost::random::uniform_int_distribution<> dist(-100, 100); for (std::size_t l = 0; l < n; ++l) A(l, l) = T(dist(rng)); for (std::size_t l = 0; l < (n - 1); ++l) { for (std::size_t i = l + 1; i < n; ++i) { A(i, l) = T(dist(rng)); A(l, i) = A(i, l); } } }
inline T euclidean_norm( orthotope<T> const& w ) { BOOST_ASSERT(1 == w.order()); T sum = T(); for (std::size_t i = 0; i < w.extent(0); ++i) sum += (w(i) * w(i)); return std::sqrt(sum); }
inline void matrix_add( orthotope<T> const& A , orthotope<T> const& B ) { BOOST_ASSERT(2 == A.order()); BOOST_ASSERT(2 == B.order()); BOOST_ASSERT(A.hypercube()); BOOST_ASSERT(B.hypercube()); std::size_t const n = A.extent(0); for (std::size_t i = 0; i < n; ++i) for (std::size_t j = 0; j < n; ++j) A(i, j) += B(i, j); }
inline void random_matrix( orthotope<T>& A , std::size_t seed ) { BOOST_ASSERT(2 == A.order()); std::size_t const n = A.extent(0); std::size_t const m = A.extent(1); boost::random::mt19937_64 rng(std::time(0)); boost::random::uniform_int_distribution<> dist(-100, 100); for (std::size_t i = 0; i < n; ++i) for (std::size_t j = 0; j < m; ++j) A(i, j) = T(dist(rng)); }
void householders_tri_factor( orthotope<T>& A , std::size_t block_size , T eps = 1e-8 ) { BOOST_ASSERT(2 == A.order()); BOOST_ASSERT(A.hypercube()); std::size_t const n = A.extent(0); for (std::size_t l = 0; l < (n - 2); ++l) { boost::int16_t sign = -compute_sign(A(l + 1, l)); T alpha = 0.0; for (std::size_t j = (l + 1); j < n; ++j) alpha += (A(j, l) * A(j, l)); if (alpha < eps) continue; alpha = sign * std::sqrt(alpha); T const r = std::sqrt(0.5 * ((alpha * alpha) - (alpha * A(l + 1, l)))); orthotope<T> w({n}); w(l + 1) = (A(l + 1, l) - alpha) / (2.0 * r); for (std::size_t j = (l + 2); j < n; ++j) w(j) = A(j, l) / (2.0 * r); orthotope<T> H = compute_H(w); /// A_l = H * A_l_minus_1 * H orthotope<T> H_A_l_minus_1 = blocked_matrix_multiply(H, A, block_size); A = blocked_matrix_multiply(H_A_l_minus_1, H, block_size); } }
inline orthotope<T> matrix_multiply( orthotope<T> const& A , orthotope<T> const& B ) { BOOST_ASSERT(A.order() == 2); BOOST_ASSERT(B.order() == 2); BOOST_ASSERT(A.extent(1) == B.extent(0)); // (n x m) * (m x p) = (n x p) std::size_t const n = A.extent(0); std::size_t const m = A.extent(1); std::size_t const p = B.extent(1); orthotope<T> C({n, p}); for (std::size_t i = 0; i < n; ++i) { for (std::size_t j = 0; j < p; ++j) { T sum = T(); for (std::size_t l = 0; l < m; ++l) sum += A(i, l) * B(l, j); C(i, j) = sum; } } return C; }
inline void print( orthotope<T> const& A , std::string const& name ) { BOOST_ASSERT(2 == A.order()); for (std::size_t i = 0; i < A.extent(0); ++i) { if (i == 0) std::cout << (boost::format("%- 8s = [ ") % name); else std::cout << (boost::format("%|11T |[ ")); for (std::size_t j = 0; j < A.extent(1); ++j) output_float(A(i, j)); std::cout << "]\n"; } std::cout << "\n"; }
inline orthotope<T> compute_H( orthotope<T> const& w ) { BOOST_ASSERT(1 == w.order()); std::size_t const n = w.extent(0); orthotope<T> H({n, n}); for (std::size_t i = 0; i < n; ++i) { for (std::size_t j = 0; j < n; ++j) { if (i == j) H(i, j) = 1 - 2 * (w(i) * w(j)); else H(i, j) = 0 - 2 * (w(i) * w(j)); } } return H; }
inline T compute_sigma( orthotope<T> const& R , std::size_t n , std::size_t l ) { BOOST_ASSERT(2 == R.order()); T sum = T(); for (std::size_t i = l; i < n; ++i) sum += (R(i, l) * R(i, l)); return std::sqrt(sum); }
inline orthotope<T> blocked_matrix_multiply( orthotope<T> const& A , orthotope<T> const& B , std::size_t block_size ) { BOOST_ASSERT(2 == A.order()); BOOST_ASSERT(2 == B.order()); BOOST_ASSERT(A.hypercube()); BOOST_ASSERT(B.hypercube()); BOOST_ASSERT(0 != block_size); BOOST_ASSERT(0 == (A.extent(0) % block_size)); std::size_t const n = A.extent(0); orthotope<T> C({n, n}); // TODO: Figure out how large this will be and do a reserve. std::vector<hpx::lcos::future<void> > stop_list; for (std::size_t i = 0; i < n; i += block_size) { for (std::size_t j = 0; j < n; j += block_size) { orthotope<T> C_sub(C, {block_size, block_size}, {i, j}); matrix_mutex mtx; for (std::size_t l = 0; l < n; l += block_size) { orthotope<T> A_sub(A, {block_size, block_size}, {i, l}) , B_sub(B, {block_size, block_size}, {l, j}); stop_list.push_back( hpx::async<multiply_and_add_action>( hpx::find_here(), C_sub, A_sub, B_sub, mtx ).get_future()); } } } hpx::lcos::wait(stop_list); return C; }
inline bool matrix_equal( orthotope<T> const& A , orthotope<T> const& B ) { // Are A and B both 2 dimensional? if ((2 != A.order()) || (2 != B.order())) return false; // Do A and B have the same dimensions? if ((A.extent(0) != B.extent(0)) || (A.extent(1) != B.extent(1))) return false; std::size_t const n = A.extent(0); std::size_t const m = A.extent(1); for (std::size_t i = 0; i < n; ++i) for (std::size_t j = 0; j < m; ++j) if (!compare_floating(A(i, j), B(i, j))) return false; return true; }
void householders( orthotope<T> const& A ) { BOOST_ASSERT(2 == A.order()); BOOST_ASSERT(A.hypercube()); std::size_t const n = A.extent(0); orthotope<T> R = A.copy(); orthotope<T> Q({n, n}); for (std::size_t l = 0; l < n; ++l) Q(l, l) = 1.0; for (std::size_t l = 0; l < (n - 1); ++l) { T const sigma = compute_sigma(R, n, l); boost::int16_t const sign = compute_sign(R(l, l)); #if defined(HPXLA_DEBUG_HOUSEHOLDERS) std::cout << (std::string(80, '#') + "\n") << "ROUND " << l << "\n\n"; print(sigma, "sigma"); print(sign, "sign"); #endif orthotope<T> w({n}); w(l) = R(l, l) + sign * sigma; for (std::size_t i = (l + 1); i < w.extent(0); ++i) w(i) = R(i, l); #if defined(HPXLA_DEBUG_HOUSEHOLDERS) print(w, "u"); #endif T const w_norm = euclidean_norm(w); for (std::size_t i = l; i < n; ++i) w(i) /= w_norm; #if defined(HPXLA_DEBUG_HOUSEHOLDERS) print(w, "v"); #endif orthotope<T> H = compute_H(w); #if defined(HPXLA_DEBUG_HOUSEHOLDERS) print(H, "H"); #endif R = matrix_multiply(H, R); Q = matrix_multiply(Q, H); for (std::size_t i = l + 1; i < n; ++i) R(i, l) = 0; } #if defined(HPXLA_DEBUG_HOUSEHOLDERS) std::cout << std::string(80, '#') << "\n"; #endif print(A, "A"); print(Q, "Q"); print(R, "R"); #if defined(HPXLA_DEBUG_HOUSEHOLDERS) check_QR(A, Q, R); #endif }
void check_QR( orthotope<T> const& A , orthotope<T> const& Q , orthotope<T> const& R ) { // {{{ BOOST_ASSERT(2 == A.order()); BOOST_ASSERT(2 == Q.order()); BOOST_ASSERT(2 == R.order()); BOOST_ASSERT(A.hypercube()); BOOST_ASSERT(Q.hypercube()); BOOST_ASSERT(R.hypercube()); std::size_t const n = A.extent(0); BOOST_ASSERT(n == Q.extent(0)); BOOST_ASSERT(n == R.extent(0)); /////////////////////////////////////////////////////////////////////////// /// Make sure Q * R equals A. orthotope<T> QR = matrix_multiply(Q, R); for (std::size_t l = 0; l < n; ++l) { for (std::size_t i = 0; i < n; ++i) { if (!compare_floating(A(l, i), QR(l, i), 1e-6)) std::cout << "WARNING: QR[" << l << "][" << i << "] (value " << QR(l, i) << ") is not equal to A[" << l << "][" << i << "] (value " << A(l, i) << ")\n"; } } /////////////////////////////////////////////////////////////////////////// /// Make sure R is an upper triangular matrix. for (std::size_t l = 0; l < (n - 1); ++l) { for (std::size_t i = l + 1; i < n; ++i) { if (!compare_floating(0.0, R(i, l), 1e-6)) std::cout << "WARNING: R[" << i << "][" << l << "] is not 0 " "(value is " << R(i, l) << "), R is not an upper " "triangular matrix\n"; } } /////////////////////////////////////////////////////////////////////////// /// Make sure Q is orthogonal. A matrix is orthogonal if its transpose is /// equal to its inverse: /// /// Q^T = Q^-1 /// /// This implies that: /// /// Q^T * Q = Q * Q^T = I /// /// We use the above formula to verify Q's orthogonality. orthotope<T> QT = Q.copy(); // Transpose QT. for (std::size_t l = 0; l < (n - 1); ++l) for (std::size_t i = l + 1; i < n; ++i) std::swap(QT(l, i), QT(i, l)); // Compute Q^T * Q and store the result in QT. QT = matrix_multiply(Q, QT); for (std::size_t l = 0; l < n; ++l) { for (std::size_t i = 0; i < n; ++i) { // Diagonals should be 1. if (l == i) { if (!compare_floating(1.0, QT(l, i), 1e-6)) std::cout << "WARNING: (Q^T * Q)[" << l << "][" << i << "] " "is not 1 (value is " << QT(l, i) << "), Q is " "not an orthogonal matrix\n"; } // All other entries should be 0. else { if (!compare_floating(0.0, QT(l, i), 1e-6)) std::cout << "WARNING: (Q^T * Q)[" << l << "][" << i << "] " "is not 0 (value is " << QT(l, i) << "), Q is " "not an orthogonal matrix\n"; } } } } // }}}
std::vector<std::complex<T> > qr_eigenvalue( orthotope<T> const& A , std::size_t max_iterations , std::size_t block_size , T const& tolerance = 1.0 ) { BOOST_ASSERT(2 == A.order()); BOOST_ASSERT(A.hypercube()); std::size_t const n = A.extent(0); /* std::vector<std::complex<T> > evs; evs.reserve(n); */ std::complex<T> const nan_(std::numeric_limits<T>::quiet_NaN() , std::numeric_limits<T>::quiet_NaN()); std::vector<std::complex<T> > evs(n, nan_), old(n, nan_); orthotope<T> Ak = A.copy(), R, Q; householders_tri_factor(Ak, block_size); write_matrix_to_octave_file(Ak, "hess_A0"); std::size_t iterations = 0; while (true) { /* T const mu = Ak(n, n); if (0 != iterations) { for (std::size_t i = 0; i < (n - 1); ++i) Ak(i, i) -= mu; Ak(n, n) = 0.0; } */ householders_qr_factor(Ak, Q, R, block_size); Ak = blocked_matrix_multiply(R, Q, block_size); /* if (0 != iterations) for (std::size_t i = 0; i < n; ++i) Ak(i, i) += mu; */ /* bool pseudo_upper_triangular = true; for (std::size_t j = 0; j < (n - 1); ++j) { // Make sure we're in Hessenberg form. for (std::size_t i = j + 2; i < n; ++i) { if (!compare_floating(0.0, Ak(i, j), 1e-6)) pseudo_upper_triangular = false; } /// Check for convergence. Either we converge to 2x2 complex /// conjugates eigenvalues, which take the form: /// /// [ a b ] /// [ c a ] /// /// Where b * c < 0. Or, we converge to real eigenvalues which take /// the form: /// /// [ a ] /// [ 0 ] /// // Determine if we've failed to converge to a real eigenvalue. if (!compare_floating(0.0, Ak(j, j + 1), 1e-6)) { // Determine if we've failed to converge to a pair of complex // eigenvalues. if (!compare_floating(Ak(j, j), Ak(j + 1, j + 1), 1e-6)) pseudo_upper_triangular = false; } } */ bool converged = true; // std::cout << "ITERATION " << iterations << "\n"; for (std::size_t j = 0; j < n; ++j) { if (j != n) { // Check for complex eigenvalues. if (!compare_floating(0.0, Ak(j + 1, j), 1e-6)) { T const test0 = 4.0 * Ak(j, j + 1) * Ak(j + 1, j) + ( (Ak(j, j) - Ak(j + 1, j + 1)) * (Ak(j, j) - Ak(j + 1, j + 1))); // Check if solving the eigenvalues of the 2x2 matrix // will give us a complex answer. This avoids unnecessary // square roots. if (-1 == compute_sign(test0)) { std::complex<T> const a(Ak(j, j) ); std::complex<T> const d(Ak(j + 1, j + 1)); std::complex<T> const i2(2.0); std::complex<T> const comp_part = std::sqrt(std::complex<T>(test0)); evs[j ] = (a + d) / i2 + comp_part / i2; evs[j + 1] = (a + d) / i2 - comp_part / i2; /* std::cout << "evs[" << j << "] = " << evs[j] << "\n" << "old[" << j << "] = " << old[j] << "\n" << "evs[" << (j + 1) << "] = " << evs[j + 1] << "\n" << "old[" << (j + 1) << "] = " << old[j + 1] << "\n"; */ if ((old[j] == nan_) || (old[j + 1] == nan_)) converged = false; else { T const evs0_real = evs[j ].real(); T const evs0_imag = evs[j ].imag(); T const evs1_real = evs[j + 1].real(); T const evs1_imag = evs[j + 1].imag(); T const old0_real = old[j ].real(); T const old0_imag = old[j ].imag(); T const old1_real = old[j + 1].real(); T const old1_imag = old[j + 1].imag(); bool const test1 = compare_are(old0_real, evs0_real, 0.1) && compare_are(old0_imag, evs0_imag, 0.1) && compare_are(old1_real, evs1_real, 0.1) && compare_are(old1_imag, evs1_imag, 0.1); /* std::cout << "test = " << test1 << "\n"; std::cout << "converged = " << converged << "\n"; */ converged = converged && test1; } /* std::cout << "converged[" << j << "] = " << converged << "\n"; std::cout << "converged[" << (j + 1) << "] = " << converged << "\n"; */ // We handled Ak(j + 1, j + 1), so skip the next // iteration. ++j; continue; } } } evs[j] = Ak(j, j); /* std::cout << "evs[" << j << "] = " << evs[j] << "\n" << "old[" << j << "] = " << old[j] << "\n"; */ if (old[j] == nan_) converged = false; else { T const evs_real = evs[j].real(); T const evs_imag = evs[j].imag(); T const old_real = old[j].real(); T const old_imag = old[j].imag(); bool const test = compare_are(old_real, evs_real, 0.1) && compare_are(old_imag, evs_imag, 0.1); /* std::cout << "test = " << test << "\n"; std::cout << "converged = " << converged << "\n"; */ converged = converged && test; } /* std::cout << "converged[" << j << "] = " << converged << "\n"; */ } old = evs; ++iterations; if (converged) break; /* if (pseudo_upper_triangular) break; */ if (iterations >= max_iterations) { std::cout << "Didn't converge in " << max_iterations << " iterations\n"; write_matrix_to_octave_file(Ak, "best_Ak"); return std::vector<std::complex<T> >(); } } std::cout << "Converged in " << iterations << " iterations\n"; write_matrix_to_octave_file(Ak, "Ak"); /* for (std::size_t i = 0; i < n; ++i) { if (i != n) { // Check for complex eigenvalues. if (!compare_floating(0.0, Ak(i + 1, i), 1e-6)) { /// For a 2x2 matrix: /// /// A = [a b] /// [c d] /// /// The eigenvalues are: /// /// e0 = (a + d) / 2 + sqrt(4 * b * c + (a - d) ^ 2) / 2 /// e1 = (a + d) / 2 - sqrt(4 * b * c + (a - d) ^ 2) / 2 /// std::complex<T> e0, e1; std::complex<T> const a(Ak(i, i) ); std::complex<T> const b(Ak(i, i + 1)); std::complex<T> const c(Ak(i + 1, i) ); std::complex<T> const d(Ak(i + 1, i + 1)); std::complex<T> const i2(2.0); std::complex<T> const i4(4.0); using std::sqrt; e0 = (a + d) / i2 + sqrt(i4 * b * c + (a - d) * (a - d)) / i2; e1 = (a + d) / i2 - sqrt(i4 * b * c + (a - d) * (a - d)) / i2; evs.push_back(e0); evs.push_back(e1); // Ak(i + 1, i + 1) is also a complex eigenvalue, so we skip it // next iteration. ++i; continue; } } evs.push_back(std::complex<T>(Ak(i, i))); } */ return evs; }