3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-07 18:05:21 +00:00
z3/lib/lu.cpp
Leonardo de Moura e9eab22e5c Z3 sources
Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
2012-10-02 11:35:25 -07:00

1793 lines
55 KiB
C++

/*++
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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::todo::display(std::ostream & out) const {
vector<unsigned_vector>::const_iterator it = m_elems_per_len.begin();
vector<unsigned_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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
lu<NM>::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<typename NM>
lu<NM>::~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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
inline unsigned lu<NM>::fillin_for(unsigned sz) {
return (sz / INV_FILLIN_FACTOR) + 1;
}
template<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
bool lu<NM>::check_locw() const {
for (unsigned i = 0; i < m_sz; i++) {
SASSERT(locw[i] == UINT_MAX);
}
return true;
}
template<typename NM>
inline void lu<NM>::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<typename NM>
inline void lu<NM>::inc_lenr(unsigned r) {
SASSERT(enabled_row(r));
m_todo_rows.updt_len(r, m_todo_rows.len(r) + 1);
}
template<typename NM>
inline void lu<NM>::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<typename NM>
inline void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::check_P() const {
SASSERT(P.check_invariant());
return true;
}
template<typename NM>
bool lu<NM>::check_Q() const {
SASSERT(Q.check_invariant());
return true;
}
template<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
void lu<NM>::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 << "<deleted>*x" << c;
}
}
out << "\n";
}
}
template<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
lu<NM>::dense_vector::dense_vector(manager & m, unsigned sz):
m_manager(m) {
m_in_non_zeros.resize(sz, false);
m_values.resize(sz);
}
template<typename NM>
lu<NM>::dense_vector::~dense_vector() {
reset();
}
template<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::dense_vector::reset(unsigned new_sz) {
reset();
m_in_non_zeros.resize(new_sz, false);
m_values.resize(new_sz);
}
template<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::compress_U_if_needed() {
if (U.indr.size() > COMPRESSION_FACTOR * U.num_entries) {
compress_U_rows();
CASSERT("lu", check_U());
}
}
template<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
unsigned lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
bool lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<typename NM>
void lu<NM>::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<unsynch_mpq_manager>;
template class lu<double_manager>;