/*++ Copyright (c) 2011 Microsoft Corporation Module Name: lu.cpp Abstract: Simple LU factorization module based on the paper: "Maintaining LU factors of a General Sparse Matrix" P. E. Gill, W. Murray, M. Saunders, M. Wright Author: Leonardo de Moura (leonardo) 2011-06-09 Revision History: --*/ #include"lu.h" template void lu::todo::init(unsigned capacity) { m_elem2len.reset(); m_elem2pos.reset(); m_elems_per_len.reset(); m_elem2len.resize(capacity, UINT_MAX); m_elem2pos.resize(capacity, UINT_MAX); m_elems_per_len.resize(capacity+1); m_size = 0; } template void lu::todo::display(std::ostream & out) const { vector::const_iterator it = m_elems_per_len.begin(); vector::const_iterator end = m_elems_per_len.end(); for (unsigned idx = 0; it != end; ++it, ++idx) { unsigned_vector const & v = *it; if (!v.empty()) { out << idx << ": ["; unsigned_vector::const_iterator it2 = v.begin(); unsigned_vector::const_iterator end2 = v.end(); for (bool first = true; it2 != end2; ++it2) { if (first) first = false; else out << " "; out << *it2; } out << "] "; } } } template void lu::todo::updt_len(unsigned elem, unsigned len) { erase(elem); m_elem2len[elem] = len; unsigned pos = m_elems_per_len[len].size(); m_elems_per_len[len].push_back(elem); m_elem2pos[elem] = pos; m_size++; } template void lu::todo::erase(unsigned elem) { if (!contains(elem)) return; unsigned len = m_elem2len[elem]; unsigned pos = m_elem2pos[elem]; unsigned_vector & v = m_elems_per_len[len]; SASSERT(v[pos] == elem); if (pos != v.size() - 1) { unsigned last_elem = v.back(); m_elem2pos[last_elem] = pos; v[pos] = last_elem; } v.pop_back(); m_elem2pos[elem] = UINT_MAX; m_size--; } template void lu::todo::iterator::find_next() { unsigned sz = m_todo.m_elems_per_len.size(); while (m_i < sz) { if (m_j < m_todo.m_elems_per_len[m_i].size()) return; m_j = 0; m_i++; } } // ----------------------- // // Main // // ----------------------- template lu::lu(manager & m, params_ref const & p): m_manager(m), m_tmp_xU_vector(m, 0), m_tmp_replace_column_vector(m, 0), m_tmp_vector(m, 0), m_tmp_row(m, 0), m_tmp_col(m, 0) { m_sz = 0; CASSERT("lu", check_invariant()); ini = false; updt_params(p); } template lu::~lu() { m().del(m_mu); // temporary values m().del(tol); m().del(C_max); m().del(A_ij); m().del(A_best); m().del(A_aux); m().del(tmp); m().del(mu_best); m().del(mu_1); del_nums(L.A); del_nums(U.A); del_nums(T.A); } template void lu::updt_params(params_ref const & p) { unsigned mu = p.get_uint(":lu-mu", 100); // it was 10... m().set(m_mu, mu); m_selection_cutoff = p.get_uint(":lu-selection-cutoff", 10); } /** \brief Delete the numerals in the given vector, and reset it. */ template void lu::del_nums(numeral_vector & nums) { typename numeral_vector::iterator it = nums.begin(); typename numeral_vector::iterator end = nums.end(); for (; it != end; ++it) { m().del(*it); } nums.reset(); } template void lu::reset() { m_sz = 0; P.reset(m_sz); Q.reset(m_sz); del_nums(L.A); L.indc.reset(); L.indr.reset(); del_nums(U.A); U.indr.reset(); U.begr.reset(); U.endr.reset(); U.num_entries = 0; T.indr.reset(); T.begr.reset(); T.endr.reset(); del_nums(T.A); T.indc.reset(); T.begc.reset(); T.endc.reset(); T.num_entries = 0; locw.reset(); } // ----------------------- // // Initialization // // ----------------------- #define INI_COL_SZ 16 #define INV_FILLIN_FACTOR 5 #define COMPRESSION_FACTOR 4 template inline unsigned lu::fillin_for(unsigned sz) { return (sz / INV_FILLIN_FACTOR) + 1; } template void lu::init(unsigned size) { reset(); m_num_replacements = 0; m_sz = size; m_todo_rows.init(size); m_todo_cols.init(size); m_enabled_rows.reset(); m_enabled_cols.reset(); m_enabled_rows.resize(m_sz, true); m_enabled_cols.resize(m_sz, true); P.reset(m_sz); Q.reset(m_sz); locw.reset(); // force it to be reinitialized, it may contain unintended content if LU was interrupted due to floating point imprecision. locw.resize(m_sz, UINT_MAX); ini = size > 0; ini_irow = 0; T.begr.push_back(0); T.endr.push_back(0); T.num_entries = 0; SASSERT(T.begc.empty()); SASSERT(T.endc.empty()); T.A.resize(m_sz * INI_COL_SZ); T.indc.resize(m_sz * INI_COL_SZ, UINT_MAX); unsigned locc = 0; for (unsigned i = 0; i < m_sz; i++) { T.begc.push_back(locc); T.endc.push_back(locc); locc += INI_COL_SZ; } SASSERT(T.begc.size() == m_sz); SASSERT(T.endc.size() == m_sz); m_tmp_vector.reset(m_sz); m_tmp_col.reset(m_sz); m_tmp_xU_vector.reset(m_sz); m_tmp_replace_column_vector.reset(m_sz); } template void lu::add_entry(numeral const & a, unsigned x) { SASSERT(T.endc[x] >= T.begc[x]); SASSERT(T.endc[x] - T.begc[x] + 1 <= m_sz); // has space for another element in column x SASSERT(ini); SASSERT(x < m_sz); TRACE("lu", tout << "add_entry(" << m().to_string(a) << ", " << x << ")\n";); if (T.endc[x] == T.indc.size()) { // expand last column T.A.push_back(numeral()); m().set(T.A.back(), a); T.indc.push_back(ini_irow); T.endc[x]++; SASSERT(T.endc[x] == T.indc.size()); } else { if (T.indc[T.endc[x]] != UINT_MAX) { TRACE("lu", tout << "moving column to the end: " << x << "\n";); move_col_to_end(x); } SASSERT(T.indc[T.endc[x]] == UINT_MAX); // use free space unsigned pos = T.endc[x]; m().set(T.A[pos], a); T.indc[pos] = ini_irow; T.endc[x]++; } T.indr.push_back(x); T.endr.back()++; T.num_entries++; SASSERT(T.endc[x] >= T.begc[x]); SASSERT(T.endc[x] - T.begc[x] <= m_sz); } template void lu::move_col_to_end(unsigned c) { SASSERT(T.endc[c] >= T.begc[c]); SASSERT(T.endc[c] - T.begc[c] <= m_sz); unsigned begin = T.begc[c]; unsigned end = T.endc[c]; T.begc[c] = T.indc.size(); T.endc[c] = T.begc[c] + end - begin; for (unsigned j = begin; j < end; j++) { T.A.push_back(numeral()); m().swap(T.A.back(), T.A[j]); unsigned r = T.indc[j]; T.indc.push_back(r); T.indc[j] = UINT_MAX; // mark as free } unsigned sz = end - begin; unsigned fillin = fillin_for(sz); for (unsigned i = 0; i < fillin; i++) { T.indc.push_back(UINT_MAX); // leave space T.A.push_back(numeral()); } SASSERT(T.endc[c] >= T.begc[c]); SASSERT(T.endc[c] - T.begc[c] <= m_sz); } template void lu::move_row_to_end(unsigned r) { unsigned begin = T.begr[r]; unsigned end = T.endr[r]; T.begr[r] = T.indr.size(); T.endr[r] = T.begr[r] + end - begin; for (unsigned j = begin; j < end; j++) { unsigned c = T.indr[j]; T.indr.push_back(c); T.indr[j] = UINT_MAX; // mark as free } unsigned sz = end - begin; unsigned fillin = fillin_for(sz); for (unsigned i = 0; i < fillin; i++) T.indr.push_back(UINT_MAX); } template void lu::end_row() { TRACE("lu", tout << "end_row()\n";); SASSERT(ini); ini_irow++; if (ini_irow == m_sz) { // completed initialization ini = false; CASSERT("lu", check_T()); } else { unsigned sz = T.endr.back() - T.begr.back(); unsigned fillin = fillin_for(sz); for (unsigned i = 0; i < fillin; i++) T.indr.push_back(UINT_MAX); // leave space T.begr.push_back(T.indr.size()); T.endr.push_back(T.indr.size()); } } // ----------------------- // // Factorization // // ----------------------- // Initialize the todo vectors: todo_rows and todo_cols. // The auxiliary vectors iplocr and iplocc are also initialized. template void lu::init_fact() { for (unsigned i = 0; i < m_sz; i++) { SASSERT(T.endr[i] >= T.begr[i]); SASSERT(T.endc[i] >= T.begc[i]); SASSERT(T.endr[i] - T.begr[i] <= m_sz); SASSERT(T.endc[i] - T.begc[i] <= m_sz); m_todo_rows.updt_len(i, T.endr[i] - T.begr[i]); m_todo_cols.updt_len(i, T.endc[i] - T.begc[i]); } m_marked_rows.reset(); m_marked_rows.resize(m_sz, false); } template bool lu::stability_test(unsigned rin, unsigned cin, bool improvingM) { if (NM::precise()) { if (improvingM) { // must save coefficient of rin, cin in A_best unsigned begin = T.begc[cin]; unsigned end = T.endc[cin]; for (unsigned j = begin; j < end; j++) { unsigned r = T.indc[j]; if (rin == r) { m().set(A_best, T.A[j]); break; } } return true; } else { return false; } } // Stability test for imprecise numerals // See section 5.3 of "Maintaining LU factors of a General Sparse Matrix" m().set(C_max, 0); m().set(tol, 0); m().set(A_ij, 0); bool C_max_init = false; unsigned begin = T.begc[cin]; unsigned end = T.endc[cin]; #if 0 static unsigned stability_test_counter = 0; static unsigned stability_test_cost = 0; stability_test_counter++; stability_test_cost += end - begin; if (stability_test_counter % 1000000 == 0) { verbose_stream() << "[stability-test] cost: " << stability_test_cost << " num-calls: " << stability_test_counter << " selection-cutoff: " << m_selection_cutoff << " sz: " << m_sz << " avg-col-sz: " << stability_test_cost / stability_test_counter << "\n"; } #endif for (unsigned j = begin; j < end; j++) { unsigned r = T.indc[j]; if (rin == r) { m().set(A_ij, T.A[j]); m().set(tol, A_ij); m().abs(tol); if (improvingM) { // tol = |A_ij| / mu m().mul(tol, m_mu, tol); } else { // tol = |A_ij| / mu_best m().mul(tol, mu_best, tol); } if (C_max_init && m().ge(C_max, tol)) { TRACE("stability", tout << "failure 1. C_max: " << m().to_string(C_max) << ", tol: " << m().to_string(tol) << ", A_ij: " << m().to_string(A_ij) << ", m_mu: " << m().to_string(m_mu) << ", mu_best: " << m().to_string(mu_best) << ", improvingM: " << improvingM << "\n";); return false; } continue; } if (!enabled_row(r)) continue; m().set(A_aux, T.A[j]); m().abs(A_aux); if (m().gt(A_aux, C_max)) { m().set(C_max, A_aux); C_max_init = true; if (m().is_pos(tol)) { // if tol was already set test, then reject if C_max >= tol if (m().ge(C_max, tol)) { TRACE("stability", tout << "failure 2. C_max: " << m().to_string(C_max) << ", tol: " << m().to_string(tol) << ", A_ij: " << m().to_string(A_ij) << ", m_mu: " << m().to_string(m_mu) << ", mu_best: " << m().to_string(mu_best) << "\n";); return false; } } } } m().set(A_best, A_ij); m().set(mu_best, C_max); m().abs(A_ij); m().div(mu_best, A_ij, mu_best); CTRACE("stability", m().is_zero(mu_best), tout << "found: A_ij: " << m().to_string(A_ij) << " mu_best: " << m().to_string(mu_best) << " C_max: " << m().to_string(C_max) << "\n";); return true; } /** \brief Select the next pivot and store in (r_out, c_out) The coefficient is stored in A_best. */ template void lu::select_pivot(unsigned & r_out, unsigned & c_out) { unsigned M_best = UINT_MAX; unsigned M2_best = UINT_MAX; m().set(mu_best, 0); SASSERT(m_todo_rows.size() == m_todo_cols.size()); TRACE("lu_todo", tout << "rows: "; m_todo_rows.display(tout); tout << "\n"; tout << "cols: "; m_todo_cols.display(tout); tout << "\n";); typename todo::iterator it(m_todo_rows); unsigned counter = 0; while (!it.at_end()) { unsigned r = it.curr(); it.next(); counter++; TRACE("lu_todo", tout << "row r: " << r << ", r_out: " << r_out << ", c_out: " << c_out << "\n";); SASSERT(enabled_row(r)); if (counter >= m_selection_cutoff && M_best != UINT_MAX) break; CTRACE("stability", counter > m_selection_cutoff, tout << "counter: " << counter << " mu_best: " << m().to_string(mu_best) << " M_best: " << M_best << "\n";); unsigned lenr = m_todo_rows.len(r); unsigned begin = T.begr[r]; unsigned end = T.endr[r]; #if 0 // ----------------------------------- // enable this block of code to simulate singular matrix exception after a couple of pivoting steps... #define NUM_STEPS_UNTIL_EXCEPTION 10 static unsigned g_num_pivots = 0; if (!m().precise()) { g_num_pivots++; if (g_num_pivots == NUM_STEPS_UNTIL_EXCEPTION) { g_num_pivots = 0; throw lu_exception("singular matrix"); } } //------------------------------------ #endif if (lenr == 0) throw lu_exception("singular matrix"); for (unsigned j = begin; j < end; j++) { unsigned c = T.indr[j]; if (!enabled_col(c)) continue; unsigned lenc = m_todo_cols.len(c); unsigned M = (lenc - 1) * (lenr - 1); TRACE("lu_todo", tout << "c: " << c << ", M: " << M << ", M_best: " << M_best << "\n";); if (M > M_best) continue; bool improving; if (NM::precise()) improving = M_best == UINT_MAX || M < M_best || (M == M_best && lenc < M2_best); else improving = M_best == UINT_MAX || M < M_best; if (stability_test(r, c, improving)) { M_best = M; M2_best = lenc; r_out = r; c_out = c; CTRACE("stability", !m().precise(), tout << "mu_best: " << m().to_string(mu_best) << "\n";); } else { CTRACE("stability", !m().precise(), tout << "failed stability, improving: " << improving << " mu_best: " << m().to_string(mu_best) << "\n";); } } } if (M_best == UINT_MAX) throw lu_exception("failed stability test"); typename todo::iterator it2(m_todo_cols); counter = 0; while (!it2.at_end()) { unsigned c = it2.curr(); it2.next(); counter++; TRACE("lu_todo", tout << "col c: " << c << ", r_out: " << r_out << ", c_out: " << c_out << "\n";); SASSERT(enabled_col(c)); if (counter >= m_selection_cutoff) break; unsigned lenc = m_todo_cols.len(c); unsigned begin = T.begc[c]; unsigned end = T.endc[c]; for (unsigned j = begin; j < end; j++) { unsigned r = T.indc[j]; if (!enabled_row(r)) continue; unsigned lenr = m_todo_rows.len(r); unsigned M = (lenc - 1) * (lenr - 1); TRACE("lu_todo", tout << "r: " << r << ", M: " << M << ", M_best: " << M_best << "\n";); if (M > M_best) continue; bool improving; if (NM::precise()) improving = M < M_best || (M == M_best && lenc < M2_best); else improving = M < M_best; if (stability_test(r, c, M < M_best)) { M_best = M; M2_best = lenc; r_out = r; c_out = c; } } } } /** \brief For debugging: checks whether all position in locw are UINT_MAX. */ template bool lu::check_locw() const { for (unsigned i = 0; i < m_sz; i++) { SASSERT(locw[i] == UINT_MAX); } return true; } template inline void lu::dec_lenr(unsigned r) { SASSERT(m_todo_rows.len(r) > 0); SASSERT(enabled_row(r)); m_todo_rows.updt_len(r, m_todo_rows.len(r) - 1); } template inline void lu::inc_lenr(unsigned r) { SASSERT(enabled_row(r)); m_todo_rows.updt_len(r, m_todo_rows.len(r) + 1); } template inline void lu::dec_lenc(unsigned c) { SASSERT(m_todo_cols.len(c) > 0); SASSERT(enabled_col(c)); m_todo_cols.updt_len(c, m_todo_cols.len(c) - 1); } template inline void lu::inc_lenc(unsigned c) { SASSERT(enabled_col(c)); m_todo_cols.updt_len(c, m_todo_cols.len(c) + 1); } /** \brief Remove the disabled columns from the row r. */ template void lu::del_disabled_cols(unsigned r) { unsigned begin = T.begr[r]; unsigned end = T.endr[r]; unsigned j = begin; for (unsigned i = begin; i < end; i++) { unsigned c = T.indr[i]; if (!enabled_col(c)) continue; T.indr[j] = c; j++; } T.endr[r] = j; for (; j < end; j++) T.indr[j] = UINT_MAX; } /** \brief Remove colum c from row r. It also removes any disabled column. */ template void lu::del_row_entry(unsigned r, unsigned c) { unsigned begin = T.begr[r]; unsigned end = T.endr[r]; TRACE("del_row_entry", tout << "del_row_entry, r: " << r << ", c: " << c << ", begin: " << begin << ", end: " << end << "\n";); unsigned j = begin; for (unsigned i = begin; i < end; i++) { unsigned c_prime = T.indr[i]; if (c_prime == c || !enabled_col(c_prime)) continue; T.indr[j] = c_prime; j++; } SASSERT(j < end); T.endr[r] = j; for (; j < end; j++) T.indr[j] = UINT_MAX; TRACE("del_row_entry", tout << "after del_row_entry, begin: " << T.begr[r] << ", end: " << T.endr[r] << "\n";); } /** \brief Compress T rows */ template void lu::compress_rows() { unsigned_vector new_indr; for (unsigned r = 0; r < m_sz; r++) { unsigned begin = T.begr[r]; unsigned end = T.endr[r]; T.begr[r] = new_indr.size(); for (unsigned i = begin; i < end; i++) { new_indr.push_back(T.indr[i]); } T.endr[r] = new_indr.size(); unsigned fillin = T.endr[r] - T.begr[r]; for (unsigned i = 0; i < fillin; i++) T.indr.push_back(UINT_MAX); } T.indr.swap(new_indr); TRACE("lu_bug", tout << "compressed rows\n";); CASSERT("lu", check_T()); } /** \brief Compress T columns */ template void lu::compress_columns() { CASSERT("lu", check_T()); unsigned_vector new_indc; numeral_vector new_A; for (unsigned c = 0; c < m_sz; c++) { unsigned begin = T.begc[c]; unsigned end = T.endc[c]; T.begc[c] = new_indc.size(); for (unsigned i = begin; i < end; i++) { new_A.push_back(numeral()); m().swap(new_A.back(), T.A[i]); new_indc.push_back(T.indc[i]); } T.endc[c] = new_indc.size(); unsigned fillin = T.endc[c] - T.begc[c]; for (unsigned i = 0; i < fillin; i++) T.indc.push_back(UINT_MAX); } T.indc.swap(new_indc); T.A.swap(new_A); // I don't need to delete the elements of new_A, since I did not use m().set, but m().swap. TRACE("lu_bug", tout << "compressed columns\n";); CASSERT("lu", check_T()); } template void lu::compress_if_needed() { if (T.indr.size() > COMPRESSION_FACTOR * T.num_entries) compress_rows(); if (T.indc.size() > COMPRESSION_FACTOR * T.num_entries) compress_columns(); CASSERT("lu", check_lenr() && check_lenc()); } template void lu::add_row_entry(unsigned r, unsigned c) { if (T.endr[r] == T.indr.size()) { // expand last row T.indr.push_back(c); T.endr[r]++; SASSERT(T.endr[r] == T.indr.size()); } else { if (T.indr[T.endr[r]] != UINT_MAX) move_row_to_end(r); // use free space SASSERT(T.indr[T.endr[r]] == UINT_MAX); T.indr[T.endr[r]] = c; T.endr[r]++; } } template void lu::add_col_entry(unsigned r, unsigned c, numeral const & a) { if (T.endc[c] == T.indc.size()) { // expand last column T.A.push_back(numeral()); m().set(T.A.back(), a); T.indc.push_back(r); T.endc[c]++; SASSERT(T.endc[c] == T.indc.size()); } else { if (T.indc[T.endc[c]] != UINT_MAX) move_col_to_end(c); SASSERT(T.indc[T.endc[c]] == UINT_MAX); // use free space unsigned pos = T.endc[c]; m().set(T.A[pos], a); T.indc[pos] = r; T.endc[c]++; } } template void lu::process_pivot_core(unsigned r, unsigned c) { SASSERT(m_todo_cols.len(c) > 0); if (m_todo_cols.len(c) == 1) { // simple case... just update lenc for every c_prime != c in r. unsigned begin = T.begr[r]; unsigned end = T.endr[r]; for (unsigned k = begin; k < end; k++) { unsigned c_prime = T.indr[k]; if (c_prime != c) { SASSERT(enabled_col(c_prime)); dec_lenc(c_prime); } } return; } #ifdef Z3DEBUG unsigned num_set_locw = 0; #endif // Compute multipliers and update L m_toadd_rows.reset(); unsigned begin = T.begc[c]; unsigned end = T.endc[c]; for (unsigned k = begin; k < end; k++) { unsigned r_prime = T.indc[k]; if (r_prime == r) { SASSERT(m().eq(A_best, T.A[k])); continue; } if (!enabled_row(r_prime)) continue; // mu_1 = - A_(r_prime, c) / A_(r,c) m().set(mu_1, T.A[k]); m().div(mu_1, A_best, mu_1); // hack: mu_1 contains A_(r_prime, c) / A_(r,c) at this point // since we have to store -mu_1 in L // store (- mu_1, r_prime, r) in L L.A.push_back(numeral()); m().set(L.A.back(), mu_1); // r_prime is the row being updated // with r_prime += mu_1 * r L.indc.push_back(r_prime); L.indr.push_back(r); m().neg(mu_1); // Now, mu_1 contains -A_(r_prime, c) / A_(r,c) // little hack: temporarily store mu_1 at T.A[k] and save position at locw m().set(T.A[k], mu_1); locw[r_prime] = k; DEBUG_CODE(num_set_locw++;); m_toadd_rows.push_back(r_prime); SASSERT(T_row_contains(r_prime, c)); // decrement the length of the row since column c will be removed. dec_lenr(r_prime); // the row entry (r_prime, c) is actually removed by del_disabled_cols T.num_entries--; } // Update every non-zero column of r different from c. begin = T.begr[r]; end = T.endr[r]; TRACE("dec_len_bug", tout << "row r: " << r << ", begin: " << begin << ", end: " << end << "\n";); for (unsigned k = begin; k < end; k++) { unsigned c_prime = T.indr[k]; TRACE("dec_len_bug", tout << "processing c_prime: " << c_prime << ", c: " << c << "\n";); if (c_prime == c) continue; // The row is going to be disabled, so decrementing length of c_prime from r. // Remark: don't need to do that for c, since it will be disabled. dec_lenc(c_prime); TRACE("dec_len_bug", tout << "c_prime: " << c_prime << ", lenc[c_prime] : " << m_todo_cols.len(c_prime) << "\n";); SASSERT(enabled_col(c_prime)); unsigned begin2 = T.begc[c_prime]; unsigned end2 = T.endc[c_prime]; // Find coefficient of (r, c_prime) and store it in A_aux for (unsigned i = begin2; true; i++) { SASSERT(i < end2); if (T.indc[i] == r) { m().set(A_aux, T.A[i]); break; } } // Update column unsigned j = begin2; for (unsigned i = begin2; i < end2; i++) { unsigned r_prime = T.indc[i]; if (r_prime == r // row r is not going to be modified || !enabled_row(r_prime) // row was already processed (i.e., it is already in the triangular part) || locw[r_prime] == UINT_MAX // row is not being updated ) { if (i != j) { T.indc[j] = T.indc[i]; m().set(T.A[j], T.A[i]); T.indc[i] = UINT_MAX; } j++; continue; } // mark row as visited m_marked_rows[r_prime] = true; // update T.A[j] with T.A[i] + mu_1 * A_aux, // where mu_1 is stored at T.A[locw[r_prime]] m().addmul(T.A[i], T.A[locw[r_prime]], A_aux, T.A[j]); T.indc[j] = T.indc[i]; if (i != j) T.indc[i] = UINT_MAX; if (m().is_zero(T.A[j])) { // entry was canceled dec_lenr(r_prime); dec_lenc(c_prime); T.indc[j] = UINT_MAX; del_row_entry(r_prime, c_prime); T.num_entries--; } else { j++; } } T.endc[c_prime] = j; // Process rows that were not visited. unsigned num = m_toadd_rows.size(); for (unsigned i = 0; i < num; i++) { unsigned r_prime = m_toadd_rows[i]; if (m_marked_rows[r_prime]) { // row was already added m_marked_rows[r_prime] = false; continue; } // add entry (r_prime, c_prime) with coefficient mu_1 * A_aux // where mu_1 is stored at T.A[locw[r_prime]] add_row_entry(r_prime, c_prime); m().mul(T.A[locw[r_prime]], A_aux, tmp); add_col_entry(r_prime, c_prime, tmp); inc_lenr(r_prime); inc_lenc(c_prime); T.num_entries++; } } // Reset column c begin = T.begc[c]; end = T.endc[c]; unsigned j = begin; for (unsigned k = begin; k < end; k++) { unsigned r_prime = T.indc[k]; if (r_prime == r || !enabled_row(r_prime)) { if (j != k) { T.indc[j] = T.indc[k]; m().set(T.A[j], T.A[k]); T.indc[k] = UINT_MAX; } j++; continue; } SASSERT(locw[r_prime] != UINT_MAX); locw[r_prime] = UINT_MAX; DEBUG_CODE(num_set_locw--;); } SASSERT(num_set_locw == 0); T.endc[c] = j; } template void lu::process_pivot(unsigned i, unsigned r, unsigned c) { CASSERT("lu", check_locw()); del_disabled_cols(r); SASSERT(T.begr[r] < T.endr[r]); process_pivot_core(r, c); m_todo_rows.erase(r); m_todo_cols.erase(c); m_enabled_rows[r] = false; m_enabled_cols[c] = false; P.swap(i, P.inv(r)); Q.swap(i, Q.inv(c)); CASSERT("lu", check_locw()); } template void lu::copy_T_to_U() { U.num_entries = T.num_entries; U.begr.resize(m_sz); U.endr.resize(m_sz); // reserve space for each row unsigned pos = 0; for (unsigned r = 0; r < m_sz; r++) { U.begr[r] = pos; U.endr[r] = pos; unsigned r_sz = T.endr[r] - T.begr[r]; pos += r_sz; pos += fillin_for(r_sz); } U.A.resize(pos); U.indr.resize(pos, UINT_MAX); // fill rows for (unsigned c = 0; c < m_sz; c++) { unsigned c_prime = Q(c); // make sure the first element in each row is the pivot; unsigned begin = T.begc[c_prime]; unsigned end = T.endc[c_prime]; CTRACE("lu_bug", end < begin + 1 || end > begin + c + 1, tout << "begin: " << begin << ", end: " << end << ", c: " << c << ", c_prime: " << c_prime << "\n"; display_T(tout); tout << "P: " << P << "\nQ: " << Q << "\n";); SASSERT(end >= begin + 1); SASSERT(end <= begin + c + 1); for (unsigned t_idx = begin; t_idx < end; t_idx++) { unsigned r = T.indc[t_idx]; unsigned u_idx = U.endr[r]; SASSERT(U.indr[u_idx] == UINT_MAX); m().swap(U.A[u_idx], T.A[t_idx]); U.indr[u_idx] = c_prime; U.endr[r]++; } } SASSERT(check_U()); } template void lu::fact() { SASSERT(!ini); TRACE("fact", display_T(tout);); init_fact(); CASSERT("lu", check_lenr() && check_lenc()); unsigned r, c; for (unsigned i = 0; i < m_sz; i++) { select_pivot(r, c); TRACE("lu_pivot", tout << "pivot: " << r << " " << c << "\n";); process_pivot(i, r, c); TRACE("lu_pivot", tout << "P: " << P << "\nQ: " << Q << "\n";); CASSERT("lu", check_lenr() && check_lenc()); TRACE("lu_pivot_detail", display_T(tout);); compress_if_needed(); } copy_T_to_U(); } // During factorization, the following invariant must hold. // For every row r, enabled_row(r) => m_todo_rows.len(r) == number of enabled columns in r. // For every column c, enabled_col(c) => m_todo_cols.len(c) == number of enabled rows in c. template bool lu::check_lenr() const { for (unsigned r = 0; r < m_sz; r++) { if (enabled_row(r)) { unsigned len = 0; unsigned begin = T.begr[r]; unsigned end = T.endr[r]; for (unsigned k = begin; k < end; k++) { unsigned c = T.indr[k]; CTRACE("lu_bug", c == UINT_MAX, tout << "r: " << r << ", c: " << c << "\n";); if (enabled_col(c)) len++; } CTRACE("lu_bug", m_todo_rows.len(r) != len, tout << "failed for row: " << r << " len: " << len << " lenr[r]: " << m_todo_rows.len(r) << "\n"; display_T(tout);); SASSERT(m_todo_rows.len(r) == len); } } return true; } template bool lu::check_lenc() const { for (unsigned c = 0; c < m_sz; c++) { if (enabled_col(c)) { unsigned len = 0; unsigned begin = T.begc[c]; unsigned end = T.endc[c]; for (unsigned k = begin; k < end; k++) { unsigned r = T.indc[k]; if (enabled_row(r)) len++; } CTRACE("lu_bug", m_todo_cols.len(c) != len, tout << "failed for column: " << c << " len: " << len << " lenc[c]: " << m_todo_cols.len(c) << "\n"; display_T(tout);); SASSERT(m_todo_cols.len(c) == len); } } return true; } // ----------------------- // // Invariants // // ----------------------- template bool lu::check_P() const { SASSERT(P.check_invariant()); return true; } template bool lu::check_Q() const { SASSERT(Q.check_invariant()); return true; } template bool lu::check_L() const { SASSERT(L.A.size() == L.indc.size()); SASSERT(L.A.size() == L.indr.size()); for (unsigned i = 0; i < L.A.size(); i++) { SASSERT(L.indc[i] < m_sz); SASSERT(L.indr[i] < m_sz); } return true; } template bool lu::check_U() const { unsigned num_entries = 0; SASSERT(U.begr.size() == m_sz); SASSERT(U.endr.size() == m_sz); for (unsigned r = 0; r < m_sz; r++) { SASSERT(U.begr[r] <= U.endr[r]); SASSERT(U.endr[r] <= U.A.size()); for (unsigned j = U.begr[r]; j < U.endr[r]; j++) { num_entries++; unsigned c = U.indr[j]; SASSERT(c < m_sz); // valid variable/column // it is really upper triangular CTRACE("lu_bug", Q.inv(c) < P.inv(r), tout << "c: " << c << ", r: " << r << ", Q.inv(c): " << Q.inv(c) << ", P.inv(r): " << P.inv(r) << "\n"; display_U(tout);); SASSERT(Q.inv(c) >= P.inv(r)); } } SASSERT(num_entries == U.num_entries); return true; } /** \brief Return true if the T column c contains a reference to row r. */ template bool lu::T_col_contains(unsigned c, unsigned r) const { for (unsigned i = T.begc[c]; i < T.endc[c]; i++) if (T.indc[i] == r) return true; return false; } /** \brief Return true if the T row r contains a reference to column c. */ template bool lu::T_row_contains(unsigned r, unsigned c) const { for (unsigned i = T.begr[r]; i < T.endr[r]; i++) if (T.indr[i] == c) return true; return false; } template bool lu::check_T() const { SASSERT(T.begr.size() == m_sz); SASSERT(T.endr.size() == m_sz); SASSERT(T.A.size() == T.indc.size()); SASSERT(T.begc.size() == m_sz); SASSERT(T.endc.size() == m_sz); for (unsigned i = 0; i < m_sz; i++) { SASSERT(T.begr[i] <= T.endr[i]); SASSERT(T.endr[i] <= T.indr.size()); for (unsigned j = T.begr[i]; j < T.endr[i]; j++) { if (enabled_col(T.indr[j])) { SASSERT(T.indr[j] < m_sz); SASSERT(T_col_contains(T.indr[j], i)); } } SASSERT(T.begc[i] <= T.endc[i]); SASSERT(T.endc[i] <= T.indc.size()); for (unsigned j = T.begc[i]; j < T.endc[i]; j++) { if (enabled_row(T.indc[j])) { SASSERT(T.indc[j] < m_sz); CTRACE("lu_bug", !T_row_contains(T.indc[j], i), tout << "T.indc[j]: " << T.indc[j] << ", i: " << i << "\n"; display_T(tout);); SASSERT(T_row_contains(T.indc[j], i)); } } } return true; } template bool lu::check_invariant() const { SASSERT(check_P()); SASSERT(check_Q()); if (!ini) { SASSERT(check_L()); SASSERT(check_U()); } SASSERT(locw.size() == m_sz); return true; } template void lu::display_T(std::ostream & out) const { for (unsigned r = 0; r < m_sz; r++) { unsigned begin_r = T.begr[r]; unsigned end_r = T.endr[r]; for (unsigned j = begin_r; j < end_r; j++) { unsigned c = T.indr[j]; if (j > begin_r) out << " "; unsigned begin_c = T.begc[c]; unsigned end_c = T.endc[c]; unsigned i; for (i = begin_c; i < end_c; i++) { if (T.indc[i] == r) { // found coeff out << m().to_string(T.A[i]) << "*x" << c; break; } } if (i == end_c) { out << "*x" << c; } } out << "\n"; } } template void lu::display_U(std::ostream & out, unsigned_vector const * var_ids) const { out << "U:\n"; for (unsigned i = 0; i < m_sz; i++) { unsigned begin = U.begr[i]; unsigned end = U.endr[i]; for (unsigned j = begin; j < end; j++) { if (j > begin) out << " "; out << m().to_string(U.A[j]) << "*x"; if (var_ids) out << (*var_ids)[U.indr[j]]; else out << U.indr[j]; } out << "\n"; } } template void lu::display_L(std::ostream & out) const { out << "L: "; unsigned sz = L.A.size(); for (unsigned i = 0; i < sz; i++) { out << "(" << L.indc[i] << ", " << L.indr[i] << ", " << m().to_string(L.A[i]) << ")"; } out << "\n"; } template void lu::display(std::ostream & out, unsigned_vector const * var_ids) const { out << "P: " << P << "\n"; out << "Q: " << Q << "\n"; display_U(out, var_ids); display_L(out); } template lu::dense_vector::dense_vector(manager & m, unsigned sz): m_manager(m) { m_in_non_zeros.resize(sz, false); m_values.resize(sz); } template lu::dense_vector::~dense_vector() { reset(); } template void lu::dense_vector::reset() { iterator it = begin_non_zeros(); iterator end = end_non_zeros(); for (; it != end; ++it) { m().reset(m_values[*it]); m_in_non_zeros[*it] = false; } m_non_zeros.reset(); } template void lu::dense_vector::reset(unsigned new_sz) { reset(); m_in_non_zeros.resize(new_sz, false); m_values.resize(new_sz); } template void lu::solve_Lx_eq_y(dense_vector & y) { TRACE("lu_solve", tout << "before: Lx = y\n"; y.display(tout); tout << "\n";); SASSERT(y.size() == m_sz); // compatible size SASSERT(&(y.m()) == &(m())); // same manager unsigned sz = L.A.size(); unsigned k = 0; while (k < sz) { TRACE("Lx_eq_y", tout << "(" << L.indc[k] << " " << L.indr[k] << " " << m().to_string(L.A[k]) << ")\n"; y.display(tout); tout << "\n";); unsigned j_k = L.indr[k]; // L.indr contains column numbers. numeral const & y_j = y[j_k]; if (m().is_zero(y_j)) { for (; k < sz && L.indr[k] == j_k; k++) ; continue; } numeral const & mu_k = L.A[k]; unsigned i_k = L.indc[k]; // L.indc contains row numbers numeral & y_i = y.get(i_k); m().submul(y_i, mu_k, y_j, y_i); k++; } TRACE("lu_solve", tout << "after Lx = y\n"; y.display(tout); tout << "\n";); } template void lu::solve_Ux_eq_y(dense_vector & y) { TRACE("lu_solve", tout << "before: Ux = y\n"; y.display(tout); tout << "\n";); TRACE("lu_solve_PQ", tout << "P: " << P << "\nQ: " << Q << "\n";); // TODO: super-sparse case, where the number of zeros in y is much smaller than m_sz SASSERT(y.size() == m_sz); // compatible size SASSERT(&(y.m()) == &(m())); // same manager numeral delta; unsigned i = m_sz; dense_vector & x = m_tmp_xU_vector; x.reset(); while (i > 0) { --i; unsigned i_prime = P(i); unsigned j_prime = Q(i); unsigned begin = U.begr[i_prime]; unsigned end = U.endr[i_prime]; TRACE("lu_solve_bug", tout << "i_prime: " << i_prime << ", j_prime: " << j_prime << "\n"; tout << "y: "; y.display(tout); tout << "\n"; tout << "x: "; x.display(tout); tout << "\n";); SASSERT(end >= begin + 1); // row must be non empty CTRACE("lu_bug", U.indr[begin] != j_prime, tout << "i: " << i << ", i_prime: " << i_prime << ", j_prime: " << j_prime << ", U.indr[begin]: " << U.indr[begin] << "\n"; display_U(tout);); SASSERT(U.indr[begin] == j_prime); // j_prime must be in the first position if (end == begin + 1) { // row of size one if (m().is_zero(y[i_prime])) continue; numeral & x_j = x.get(j_prime); m().div(y[i_prime], U.A[begin], x_j); } else { // row has at least two elements. SASSERT(end > begin + 1); numeral const & a = U.A[begin]; m().reset(delta); for (unsigned k = begin+1; k < end; k++) { unsigned c = U.indr[k]; TRACE("lu_solve_bug", tout << "c: " << c << ", x[c]: " << m().to_string(x[c]) << ", delta: " << m().to_string(delta) << "\n";); m().addmul(delta, U.A[k], x[c], delta); } if (m().is_zero(delta) && m().is_zero(y[i_prime])) continue; numeral & x_j = x.get(j_prime); m().sub(y[i_prime], delta, x_j); m().div(x_j, a, x_j); } } y.swap(x); m().del(delta); TRACE("lu_solve", tout << "after: Ux = y\n"; y.display(tout); tout << "\n";); } template void lu::solve_xL_eq_y(dense_vector & y) { TRACE("lu_solve", tout << "before: xL = y\n"; y.display(tout); tout << "\n";); SASSERT(y.size() == m_sz); // compatible size SASSERT(&(y.m()) == &(m())); // same manager unsigned k = L.A.size(); while (k > 0) { --k; unsigned i_k = L.indc[k]; numeral const & y_i = y[i_k]; if (m().is_zero(y_i)) continue; numeral const & mu_k = L.A[k]; unsigned j_k = L.indr[k]; numeral & y_j = y.get(j_k); m().submul(y_j, mu_k, y_i, y_j); } TRACE("lu_solve", tout << "after: xL = y\n"; y.display(tout); tout << "\n";); } template void lu::solve_xU_eq_y(dense_vector & y) { // TODO: super-sparse case, where the number of zeros in y is much smaller than m_sz TRACE("lu_solve", tout << "before: xU = y\n"; y.display(tout); tout << "\n";); TRACE("lu_solve_PQ", tout << "P: " << P << "\nQ: " << Q << "\n";); SASSERT(y.size() == m_sz); // compatible size SASSERT(&(y.m()) == &(m())); // same manager dense_vector & x = m_tmp_xU_vector; x.reset(); for (unsigned i = 0; i < m_sz; i++) { unsigned i_prime = P(i); unsigned j_prime = Q(i); TRACE("lu_solve_step", tout << "i: " << i << ", P(i): " << P(i) << ", Q(i): " << Q(i) << ", x: "; x.display(tout); tout << ", y[j_prime]: " << m().to_string(y[j_prime]) << "\n";); if (!m().is_zero(y[j_prime])) { numeral & x_i = x.get(i_prime); m().add(x_i, y[j_prime], x_i); } if (m().is_zero(x[i_prime])) continue; numeral & x_i = x.get(i_prime); unsigned begin = U.begr[i_prime]; unsigned end = U.endr[i_prime]; SASSERT(end >= begin + 1); // row must be non empty SASSERT(U.indr[begin] == j_prime); numeral const & a = U.A[begin]; m().div(x_i, a, x_i); for (unsigned k = begin+1; k < end; k++) { // propagate x_i unsigned c = U.indr[k]; numeral & x_j = x.get(P(Q.inv(c))); m().submul(x_j, U.A[k], x_i, x_j); } } TRACE("lu_solve_step", tout << "x: "; x.display(tout); tout << "\n";); y.swap(x); TRACE("lu_solve", tout << "after: xU = y\n"; y.display(tout); tout << "\n";); } // ----------------------- // // Column replacement // // ----------------------- /** \brief Remove colum c from U row r. */ template void lu::del_U_row_entry(unsigned r, unsigned c) { unsigned begin = U.begr[r]; unsigned end = U.endr[r]; TRACE("del_row_entry", tout << "del_U_row_entry, r: " << r << ", c: " << c << ", begin: " << begin << ", end: " << end << "\n";); for (unsigned i = begin; i < end; i++) { unsigned c_prime = U.indr[i]; if (c_prime == c) { U.num_entries--; i++; for (; i < end; i++) { U.indr[i-1] = U.indr[i]; m().swap(U.A[i-1], U.A[i]); } U.indr[end-1] = UINT_MAX; // mark as free U.endr[r] = end-1; return; } } } /** \brief Compress U rows */ template void lu::compress_U_rows() { unsigned_vector new_indr; numeral_vector new_A; for (unsigned r = 0; r < m_sz; r++) { unsigned begin = U.begr[r]; unsigned end = U.endr[r]; U.begr[r] = new_indr.size(); for (unsigned i = begin; i < end; i++) { new_A.push_back(numeral()); m().swap(new_A.back(), U.A[i]); new_indr.push_back(U.indr[i]); } U.endr[r] = new_indr.size(); unsigned fillin = U.endr[r] - U.begr[r]; for (unsigned i = 0; i < fillin; i++) U.indr.push_back(UINT_MAX); } U.indr.swap(new_indr); U.A.swap(new_A); TRACE("lu_bug", tout << "compressed U rows\n";); CASSERT("lu", check_U()); } template void lu::compress_U_if_needed() { if (U.indr.size() > COMPRESSION_FACTOR * U.num_entries) { compress_U_rows(); CASSERT("lu", check_U()); } } template void lu::move_U_row_to_end(unsigned r) { unsigned begin = U.begr[r]; unsigned end = U.endr[r]; U.begr[r] = U.indr.size(); U.endr[r] = U.begr[r] + end - begin; for (unsigned j = begin; j < end; j++) { U.A.push_back(numeral()); m().swap(U.A.back(), U.A[j]); unsigned c = U.indr[j]; U.indr.push_back(c); U.indr[j] = UINT_MAX; // mark as free } unsigned sz = end - begin; unsigned fillin = fillin_for(sz); for (unsigned i = 0; i < fillin; i++) { U.A.push_back(numeral()); U.indr.push_back(UINT_MAX); } } template void lu::add_U_row_entry(unsigned r, unsigned c, numeral const & a) { TRACE("add_U_row_entry", tout << "r: " << r << ", c: " << c << ", a: " << m().to_string(a) << " row:\n"; for (unsigned i = U.begr[r]; i < U.endr[r]; i++) tout << m().to_string(U.A[i]) << "*x" << U.indr[i] << " "; tout << "\n";); U.num_entries++; if (U.endr[r] == U.indr.size()) { // expand last row U.A.push_back(numeral()); m().set(U.A.back(), a); U.indr.push_back(c); U.endr[r]++; SASSERT(U.endr[r] == U.indr.size()); } else { if (U.indr[U.endr[r]] != UINT_MAX) move_U_row_to_end(r); // use free space SASSERT(U.indr[U.endr[r]] == UINT_MAX); unsigned pos = U.endr[r]; m().set(U.A[pos], a); U.indr[pos] = c; U.endr[r]++; } } /** \brief Remove colum c from U row r. */ template void lu::add_replace_U_row_entry(unsigned r, unsigned c, numeral const & a) { unsigned begin = U.begr[r]; unsigned end = U.endr[r]; TRACE("del_row_entry", tout << "replace_U_row_entry, r: " << r << ", c: " << c << ", begin: " << begin << ", end: " << end << "\n";); for (unsigned i = begin; i < end; i++) { unsigned c_prime = U.indr[i]; if (c_prime == c) { m().set(U.A[i], a); return; } } add_U_row_entry(r, c, a); } /** \brief Just replace the column. After the replacement PUQ is not triangular anymore. */ template unsigned lu::replace_U_column_core(unsigned j, dense_vector & new_col) { unsigned max = 0; // Traverse rows in pivotal order, removing/updating entries. // We can stop at Q.inv(j) because: // For every entry (r,c) in U Q.inv(c) >= P.inv(r) // Thus j does not occur in any r' such that Q.inv(j) < P.inv(r') unsigned stop_at = Q.inv(j); for (unsigned i = 0; i <= stop_at; i++) { unsigned r = P(i); if (m().is_zero(new_col[r])) { del_U_row_entry(r, j); } else { max = i; // replace/add add_replace_U_row_entry(r, j, new_col[r]); // reset entry in new_col numeral & v = new_col.get(r); m().reset(v); } } // Add remaining rows of new_col to U unsigned_vector::const_iterator it = new_col.begin_non_zeros(); unsigned_vector::const_iterator end = new_col.m_non_zeros.end(); for (; it != end; ++it) { unsigned r = *it; if (m().is_zero(new_col[r])) continue; if (P.inv(r) > max) max = P.inv(r); add_U_row_entry(r, j, new_col[r]); } return max; } /** \brief Return true if PUQ is triangular with the exception of column c_prime. */ template bool lu::check_U_except_col(unsigned c_prime) const { for (unsigned r = 0; r < m_sz; r++) { for (unsigned j = U.begr[r]; j < U.endr[r]; j++) { unsigned c = U.indr[j]; if (c == c_prime) continue; SASSERT(Q.inv(c) >= P.inv(r)); } } return true; } /** \brief Return true if PUQ is triangular with the exception of row r_prime. */ template bool lu::check_U_except_row(unsigned r_prime) const { for (unsigned r = 0; r < m_sz; r++) { if (r == r_prime) continue; for (unsigned j = U.begr[r]; j < U.endr[r]; j++) { unsigned c = U.indr[j]; SASSERT(Q.inv(c) >= P.inv(r)); } } return true; } template void lu::replace_U_column(unsigned j, dense_vector & new_col) { TRACE("lu_replace", tout << "replacing U column j: " << j << " with\n"; new_col.display_non_zeros(tout); tout << "\n";); unsigned k = replace_U_column_core(j, new_col); if (k <= Q.inv(j)) { TRACE("lu_replace", tout << "result 1:\n"; display(tout);); CASSERT("lu", check_U()); if (m().is_zero(U.A[U.begr[P(Q.inv(j))]])) throw lu_exception("bad new column"); // column does not have the pivot. return; } TRACE("lu_replace", tout << "after replace_core:\n"; display_U(tout); tout << "P: " << P << "\n"; tout << "Q: " << Q << "\n"; tout << "Q.inv(j): " << Q.inv(j) << ", k: " << k << "\n";); // fix spike at j CASSERT("lu", check_U_except_col(j)); P.move_after(Q.inv(j), k); Q.move_after(Q.inv(j), k); TRACE("lu_replace", tout << "P: " << P << "\n"; tout << "Q: " << Q << "\n";); // fix row P_k unsigned P_k = P(k); unsigned Q_k = Q(k); CASSERT("lu", check_U_except_row(P_k)); // 1. search for smallest bad column unsigned smallest = k; unsigned begin = U.begr[P_k]; unsigned end = U.endr[P_k]; for (unsigned i = begin; i < end; i++) { unsigned c = U.indr[i]; unsigned inv_c = Q.inv(c); if (inv_c < smallest) smallest = inv_c; } if (smallest == k) { // nothing to be done TRACE("lu_replace", tout << "result 2:\n"; display(tout);); if (m().is_zero(U.A[U.begr[P_k]])) throw lu_exception("bad new column"); // column does not have the pivot. CASSERT("lu", check_U()); return; } // 2. eliminate bad columns using other rows. // 2.a. save coefficients in tmp dense vector dense_vector & row_k = m_tmp_replace_column_vector; row_k.reset(); begin = U.begr[P_k]; end = U.endr[P_k]; for (unsigned i = begin; i < end; i++) { unsigned c = U.indr[i]; U.indr[i] = UINT_MAX; // mark as free numeral & a = row_k.get(c); m().swap(a, U.A[i]); } U.num_entries -= end - begin; U.endr[P_k] = begin; // 2.b eliminate columns in m_to_fix from P_k for (; smallest < k; smallest++) { unsigned c = Q(smallest); numeral const & a_k = row_k[c]; if (m().is_zero(a_k)) continue; // use row r to eliminate c unsigned r = P(Q.inv(c)); SASSERT(U.indr[U.begr[r]] == c); numeral const & a_r = U.A[U.begr[r]]; // to eliminate c from k, we must add (-a_k/a_r * r) to row_k // Save transformation at L m().set(mu_1, a_k); m().div(mu_1, a_r, mu_1); L.A.push_back(numeral()); m().set(L.A.back(), mu_1); L.indc.push_back(P_k); L.indr.push_back(r); m().neg(mu_1); // k += mu_1 * r begin = U.begr[r]; end = U.endr[r]; for (unsigned i = begin; i < end; i++) { unsigned c_prime = U.indr[i]; numeral & a_prime = row_k.get(c_prime); if (c_prime == c) m().reset(a_prime); else m().addmul(a_prime, mu_1, U.A[i], a_prime); } } CTRACE("lu_bug", m().is_zero(row_k[Q_k]), row_k.display_non_zeros(tout); tout << "\n";); // 2.c Copy row_k back to U SASSERT(!NM::precise() || !m().is_zero(row_k[Q_k])); if (m().is_zero(row_k[Q_k])) throw lu_exception("singular matrix"); // Add pivot first SASSERT(U.begr[P_k] == U.endr[P_k]); // the row k in U is empty add_U_row_entry(P_k, Q_k, row_k[Q_k]); // Copy remaining elements typename dense_vector::iterator it3 = row_k.begin_non_zeros(); typename dense_vector::iterator end3 = row_k.end_non_zeros(); for (; it3 != end3; ++it3) { unsigned c = *it3; if (c == Q_k) continue; // already added if (m().is_zero(row_k[c])) continue; add_U_row_entry(P_k, c, row_k[c]); } TRACE("lu_replace", tout << "result 3:\n"; display(tout);); CASSERT("lu", check_U()); compress_U_if_needed(); } template void lu::replace_column(unsigned j, dense_vector & new_col) { TRACE("lu_replace", tout << "replacing column j: " << j << " with\n"; new_col.display_non_zeros(tout); tout << "\n";); SASSERT(j < m_sz); solve_Lx_eq_y(new_col); replace_U_column(j, new_col); m_num_replacements++; } // ----------------------- // // Dense vector // // ----------------------- // Make sure that every position in m_non_zeros really contains a non-zero value. template void lu::dense_vector::elim_zeros() { unsigned_vector::iterator it = m_non_zeros.begin(); unsigned_vector::iterator end = m_non_zeros.end(); unsigned_vector::iterator it2 = it; for (; it != end; ++it) { unsigned idx = *it; SASSERT(m_in_non_zeros[idx]); if (m().is_zero(m_values[idx])) { m().reset(m_values[idx]); m_in_non_zeros[idx] = false; continue; } *it2 = idx; ++it2; } m_non_zeros.set_end(it2); } template void lu::dense_vector::display(std::ostream & out) const { out << "("; unsigned sz = m_values.size(); for (unsigned i = 0; i < sz; i++) { if (i > 0) out << " "; out << m().to_string(m_values[i]); } out << ")"; } template void lu::dense_vector::display_non_zeros(std::ostream & out) const { out << "("; bool first = true; for (unsigned i = 0; i < m_non_zeros.size(); i++) { unsigned pos = m_non_zeros[i]; if (m().is_zero(m_values[pos])) continue; if (first) first = false; else out << " "; out << m().to_string(m_values[pos]) << ":" << pos; } out << ")"; } template void lu::dense_vector::display_pol(std::ostream & out) const { bool first = true; for (unsigned i = 0; i < m_non_zeros.size(); i++) { unsigned pos = m_non_zeros[i]; if (m().is_zero(m_values[pos])) continue; if (first) first = false; else out << " + "; out << m().to_string(m_values[pos]) << "*x" << pos; } } template class lu; template class lu;