/* Matrix class implementation (non-inline functions).
Copyright (C) 2001-2004 Roberto Bagnara <bagnara@cs.unipr.it>
This file is part of the Parma Polyhedra Library (PPL).
The PPL is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2 of the License, or (at your
option) any later version.
The PPL is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA.
For the most up-to-date information see the Parma Polyhedra Library
site: http://www.cs.unipr.it/ppl/ . */
#include <config.h>
#include "Matrix.defs.hh"
#include "Integer.defs.hh"
#include "Row.defs.hh"
#include "globals.hh"
#include "SatMatrix.defs.hh"
#include <algorithm>
#include <iostream>
#include <string>
#include <deque>
namespace PPL = Parma_Polyhedra_Library;
PPL::dimension_type
PPL::Matrix::num_lines_or_equalities() const {
assert(num_pending_rows() == 0);
dimension_type n = 0;
for (dimension_type i = num_rows(); i != 0; )
if (rows[--i].is_line_or_equality())
++n;
return n;
}
PPL::Matrix::Matrix(const Topology topol,
const dimension_type n_rows,
const dimension_type n_columns)
: rows(n_rows),
row_topology(topol),
row_size(n_columns),
row_capacity(compute_capacity(n_columns)),
index_first_pending(n_rows),
sorted(true) {
// Build the appropriate row type.
const Row::Type row_type(topol, Row::RAY_OR_POINT_OR_INEQUALITY);
// Construct in direct order: will destroy in reverse order.
for (dimension_type i = 0; i < n_rows; ++i)
rows[i].construct(row_type, n_columns, row_capacity);
// This will also check that `n_columns' is zero when `n_rows' is zero.
assert(OK());
}
PPL::Matrix::Matrix(const Matrix& y)
: rows(y.rows),
row_topology(y.row_topology),
row_size(y.row_size),
row_capacity(compute_capacity(y.row_size)),
index_first_pending(y.index_first_pending),
sorted(y.sorted) {
}
PPL::Matrix::Matrix(Matrix& y, const dimension_type first_stolen)
: rows(y.num_rows() - first_stolen),
row_topology(y.row_topology),
row_size(y.row_size),
row_capacity(y.row_capacity),
index_first_pending(rows.size()),
sorted(false) {
assert(first_stolen < y.num_rows());
// Steal the rows from `y', starting from `first_stolen'.
for (dimension_type i = num_rows(); i-- > 0; )
std::swap(rows[i], y.rows[first_stolen + i]);
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
// Erase from `y' the rows just swapped in from `*this'.
y.erase_to_end(first_stolen);
// Adjust the index of the first pending row, if needed.
if (y.first_pending_row() > first_stolen)
y.set_index_first_pending_row(first_stolen);
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(y.OK(false));
}
PPL::Matrix&
PPL::Matrix::operator=(const Matrix& y) {
// Without the following guard against auto-assignments we would
// recompute the row capacity based on row size, possibly without
// actually increasing the capacity of the rows. This would lead to
// an inconsistent state.
if (this != &y) {
// The following assignment may do nothing on auto-assignments...
rows = y.rows;
row_topology = y.row_topology;
row_size = y.row_size;
// ... hence the following assignment must not be done on
// auto-assignments.
row_capacity = compute_capacity(y.row_size);
index_first_pending = y.index_first_pending;
sorted = y.sorted;
}
return *this;
}
void
PPL::Matrix::set_rows_topology() {
if (is_necessarily_closed())
for (dimension_type i = num_rows(); i-- > 0; )
rows[i].set_necessarily_closed();
else
for (dimension_type i = num_rows(); i-- > 0; )
rows[i].set_not_necessarily_closed();
}
void
PPL::Matrix::grow(const dimension_type new_n_rows,
const dimension_type new_n_columns) {
const dimension_type old_n_rows = rows.size();
assert(new_n_rows >= old_n_rows);
assert(new_n_columns >= row_size);
// Note that, if we have `new_n_rows == old_n_rows', the matrix
// will keep its sortedness.
// This is obvious if `new_n_columns == row_size'.
// If `new_n_columns > row_size', then sortedness is maintained
// because trailing zeroes will be added to all rows.
const bool was_sorted = is_sorted();
if (new_n_rows > old_n_rows) {
// Rows will be inserted: build the appropriate row type.
const Row::Type row_type(row_topology, Row::RAY_OR_POINT_OR_INEQUALITY);
if (new_n_columns <= row_capacity) {
// We can recycle the old rows.
if (rows.capacity() < new_n_rows) {
// Reallocation will take place.
std::vector<Row> new_rows;
new_rows.reserve(compute_capacity(new_n_rows));
new_rows.insert(new_rows.end(), new_n_rows, Row());
// Construct the new rows.
dimension_type i = new_n_rows;
while (i-- > old_n_rows)
new_rows[i].construct(row_type, new_n_columns, row_capacity);
// Steal the old rows.
++i;
while (i-- > 0)
new_rows[i].swap(rows[i]);
// Put the new vector into place.
std::swap(rows, new_rows);
}
else {
// Reallocation will NOT take place.
rows.insert(rows.end(), new_n_rows - old_n_rows, Row());
for (dimension_type i = new_n_rows; i-- > old_n_rows; )
rows[i].construct(row_type, new_n_columns, row_capacity);
}
}
else {
// We cannot even recycle the old rows.
Matrix new_matrix(row_topology);
new_matrix.rows.reserve(compute_capacity(new_n_rows));
new_matrix.rows.insert(new_matrix.rows.end(), new_n_rows, Row());
// Construct the new rows.
new_matrix.row_size = new_n_columns;
new_matrix.row_capacity = compute_capacity(new_n_columns);
dimension_type i = new_n_rows;
while (i-- > old_n_rows)
new_matrix.rows[i].construct(row_type,
new_matrix.row_size,
new_matrix.row_capacity);
// Copy the old rows.
++i;
while (i-- > 0) {
Row new_row(rows[i],
new_matrix.row_size,
new_matrix.row_capacity);
std::swap(new_matrix.rows[i], new_row);
}
// We have the same number of pending rows as before.
new_matrix.set_index_first_pending_row(index_first_pending);
// Rows have been added: see if the matrix is known to be sorted.
new_matrix.set_sorted(old_n_rows == 0
|| (was_sorted
&& (new_matrix[old_n_rows-1]
<= new_matrix[old_n_rows])));
// Put the new vector into place.
swap(new_matrix);
return;
}
}
// Here we have the right number of rows.
if (new_n_columns > row_size) {
// We need more columns.
if (new_n_columns <= row_capacity)
// But we have enough capacity: we resize existing rows.
for (dimension_type i = old_n_rows; i-- > 0; )
rows[i].grow_no_copy(new_n_columns);
else {
// Capacity exhausted: we must reallocate the rows and
// make sure all the rows have the same capacity.
dimension_type new_row_capacity = compute_capacity(new_n_columns);
for (dimension_type i = old_n_rows; i-- > 0; ) {
Row new_row(rows[i], new_n_columns, new_row_capacity);
std::swap(rows[i], new_row);
}
row_capacity = new_row_capacity;
}
// Rows have grown or shrunk.
row_size = new_n_columns;
}
// If rows have been added, we should check if we are still sorted.
if (old_n_rows == 0)
// The matrix was empty: now it is sorted.
set_sorted(true);
else if (new_n_rows > old_n_rows)
// Rows were added.
if (was_sorted)
set_sorted((*this)[old_n_rows-1] <= (*this)[old_n_rows]);
// If no rows was added the matrix keeps its sortedness.
}
void
PPL::Matrix::resize_no_copy(const dimension_type new_n_rows,
const dimension_type new_n_columns) {
dimension_type old_n_rows = rows.size();
// Note that, if we have `new_n_rows <= old_n_rows' and
// `new_n_columns >= row_size', the matrix will keep its sortedness.
// This is obvious if `new_n_columns == row_size'.
// If `new_n_columns > row_size', then sortedness is maintained
// because trailing zeroes will be added to all rows.
if (new_n_rows > old_n_rows) {
// Rows will be inserted: build the appropriate row type.
const Row::Type row_type(row_topology, Row::LINE_OR_EQUALITY);
if (new_n_columns <= row_capacity) {
// We can recycle the old rows.
if (rows.capacity() < new_n_rows) {
// Reallocation (of vector `rows') will take place.
std::vector<Row> new_rows;
new_rows.reserve(compute_capacity(new_n_rows));
new_rows.insert(new_rows.end(), new_n_rows, Row());
// Construct the new rows (be careful: each new row must have
// the same capacity as each one of the old rows).
dimension_type i = new_n_rows;
while (i-- > old_n_rows)
new_rows[i].construct(row_type, new_n_columns, row_capacity);
// Steal the old rows.
++i;
while (i-- > 0)
new_rows[i].swap(rows[i]);
// Put the new vector into place.
std::swap(rows, new_rows);
}
else {
// Reallocation (of vector `rows') will NOT take place.
rows.insert(rows.end(), new_n_rows - old_n_rows, Row());
// Be careful: each new row must have
// the same capacity as each one of the old rows.
for (dimension_type i = new_n_rows; i-- > old_n_rows; )
rows[i].construct(row_type, new_n_columns, row_capacity);
}
// Even though `*this' may happen to keep its sortedness,
// we believe that checking such a property is not worth the effort.
// Moreover, it is very likely that the matrix will be overwritten
// as soon as we return.
set_sorted(false);
}
else {
// We cannot even recycle the old rows: allocate a new matrix and swap.
Matrix new_matrix(row_topology, new_n_rows, new_n_columns);
swap(new_matrix);
return;
}
}
else if (new_n_rows < old_n_rows) {
// Drop some rows.
rows.erase(rows.begin() + new_n_rows, rows.end());
old_n_rows = new_n_rows;
}
// Here we have the right number of rows.
if (new_n_columns != row_size) {
if (new_n_columns < row_size) {
// Shrink the existing rows.
for (dimension_type i = old_n_rows; i-- > 0; )
rows[i].shrink(new_n_columns);
// Ditto.
set_sorted(false);
}
else
// We need more columns.
if (new_n_columns <= row_capacity)
// But we have enough capacity: we resize existing rows.
for (dimension_type i = old_n_rows; i-- > 0; )
rows[i].grow_no_copy(new_n_columns);
else {
// Capacity exhausted: we must reallocate the rows and
// make sure all the rows have the same capacity.
const dimension_type new_row_capacity
= compute_capacity(new_n_columns);
const Row::Type row_type(row_topology, Row::LINE_OR_EQUALITY);
for (dimension_type i = old_n_rows; i-- > 0; ) {
Row new_row(row_type, new_n_columns, new_row_capacity);
std::swap(rows[i], new_row);
}
row_capacity = new_row_capacity;
}
// Rows have grown or shrunk.
row_size = new_n_columns;
}
}
void
PPL::Matrix::ascii_dump(std::ostream& s) const {
using std::endl;
const Matrix& x = *this;
const char separator = ' ';
s << "topology" << separator
<< (x.is_necessarily_closed() ? "" : "NOT_")
<< "NECESSARILY_CLOSED"
<< endl
<< x.num_rows() << separator << 'x' << separator
<< x.num_columns() << separator
<< (x.sorted ? "(sorted)" : "(not_sorted)")
<< endl
<< "index_first_pending " << x.first_pending_row()
<< endl;
}
bool
PPL::Matrix::ascii_load(std::istream& s) {
std::string str;
if (!(s >> str) || str != "topology")
return false;
if (!(s >> str))
return false;
if (str == "NECESSARILY_CLOSED")
set_necessarily_closed();
else {
if (str != "NOT_NECESSARILY_CLOSED")
return false;
set_not_necessarily_closed();
}
dimension_type nrows;
dimension_type ncols;
if (!(s >> nrows))
return false;
if (!(s >> str))
return false;
if (!(s >> ncols))
return false;
resize_no_copy(nrows, ncols);
if (!(s >> str) || (str != "(sorted)" && str != "(not_sorted)"))
return false;
set_sorted(str == "(sorted)");
dimension_type index;
if (!(s >> str) || str != "index_first_pending")
return false;
if (!(s >> index))
return false;
set_index_first_pending_row(index);
// Check for well-formedness.
assert(OK(true));
return true;
}
void
PPL::Matrix::merge_rows_assign(const Matrix& y) {
assert(row_size >= y.row_size);
assert(check_sorted() && y.check_sorted());
// We can use this method only when the matrices do not
// contain any pending rows.
assert(num_pending_rows() == 0 && y.num_pending_rows() == 0);
Matrix& x = *this;
// A temporary vector of rows...
std::vector<Row> tmp;
// ... with enough capacity not to require any reallocations.
tmp.reserve(compute_capacity(x.num_rows() + y.num_rows()));
std::vector<Row>::iterator xi = x.rows.begin();
const std::vector<Row>::iterator xend = x.rows.end();
std::vector<Row>::const_iterator yi = y.rows.begin();
const std::vector<Row>::const_iterator yend = y.rows.end();
while (xi != xend && yi != yend) {
const int comp = compare(*xi, *yi);
if (comp <= 0) {
// Elements that can be taken from `x' are actually _stolen_ from `x'
std::swap(*xi++, *tmp.insert(tmp.end(), Row()));
if (comp == 0)
// A duplicate element.
++yi;
}
else {
// (comp > 0)
Row copy(*yi++, row_size, row_capacity);
std::swap(copy, *tmp.insert(tmp.end(), Row()));
}
}
// Insert what is left.
if (xi != xend)
while (xi != xend)
std::swap(*xi++, *tmp.insert(tmp.end(), Row()));
else
while (yi != yend) {
Row copy(*yi++, row_size, row_capacity);
std::swap(copy, *tmp.insert(tmp.end(), Row()));
}
// We get the result vector and let the old one be destroyed.
std::swap(tmp, rows);
// There are no pending rows.
unset_pending_rows();
assert(check_sorted());
}
void
PPL::Matrix::add_pending_rows(const Matrix& y) {
Matrix& x = *this;
assert(x.row_size >= y.row_size);
const dimension_type x_n_rows = x.num_rows();
const dimension_type y_n_rows = y.num_rows();
// Grow to the required size without changing sortedness.
const bool was_sorted = sorted;
grow(x_n_rows + y_n_rows, x.row_size);
sorted = was_sorted;
// Copy the rows of `y', forcing size and capacity.
for (dimension_type i = y_n_rows; i-- > 0; ) {
Row copy(y[i], x.row_size, x.row_capacity);
std::swap(copy, x[x_n_rows+i]);
}
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::add_rows(const Matrix& y) {
assert(num_pending_rows() == 0);
// Adding no rows is a no-op.
if (y.num_rows() == 0)
return;
// Check if sortedness is preserved.
if (is_sorted())
if (y.is_sorted() && y.num_pending_rows() == 0) {
const dimension_type n_rows = num_rows();
if (n_rows > 0)
set_sorted((*this)[n_rows-1] <= y[0]);
}
// Add the rows of `y' as if they were pending.
add_pending_rows(y);
// There are no pending_rows.
unset_pending_rows();
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::sort_rows() {
const dimension_type num_pending = num_pending_rows();
// We sort the non-pending rows only.
sort_rows(0, first_pending_row());
set_index_first_pending_row(num_rows() - num_pending);
sorted = true;
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::sort_rows(const dimension_type first_row,
const dimension_type last_row) {
assert(first_row <= last_row && last_row <= num_rows());
// We cannot mix pending and non-pending rows.
assert(first_row >= first_pending_row() || last_row <= first_pending_row());
Matrix& x = *this;
// Sorting one or no rows is a no-op.
if (first_row >= last_row - 1)
return;
// CHECK ME.
// It will be often the case that we sort an almost sorted matrix.
// In such a context, insertion-sort may be more efficient than other,
// more sophisticated, sorting algorithms. We implement a variant of
// insertion-sort, in that we will remove duplicate rows.
// Duplicate rows will be first placed at the end of the sorted portion
// of the matrix and then erased. Duplicate rows will have indexes
// ranging from `new_last_row' to `last_row - 1' (included).
dimension_type new_last_row = last_row;
// In the following loop, the method `Row::assign()' will be preferred
// to `Row::swap()' for efficiency reasons whenever the old contents
// of the target row are no longer needed. Care has to be taken:
// `assign' will cause the implementation of rows to be shared,
// since it does not make a copy of the coefficients.
// A temporary Row object: it will share the implementation of `x[i]'.
Row x_i;
for (dimension_type i = first_row + 1; i < new_last_row; ) {
// Let `x_i' share the implementation of `x[i]',
// i.e., create the ``hole'' at index `i'.
x_i.assign(x[i]);
// `j' indicates the current position of the hole.
dimension_type j = i;
int cmp = 1;
while (j > first_row) {
cmp = compare(x[j-1], x_i);
if (cmp <= 0)
break;
// Move the hole one position down.
x[j].assign(x[j-1]);
--j;
}
if (cmp == 0) {
// Row `x_i' is a duplicate of `x[j-1]'.
// Move the hole back to position `i',
// preserving the sortedness of rows from `j+1' to `i'.
for ( ; j < i; ++j)
x[j].assign(x[j+1]);
// Fill the ``hole'' with `x_i'.
x[i].assign(x_i);
// Move the duplicate past the end of the portion to be sorted.
--new_last_row;
std::swap(x[i], x[new_last_row]);
// Do not increment `i', because the next row to be
// insertion-sorted is already positioned at index `i'.
}
else {
// Fill the ``hole'' with `x_i'.
x[j].assign(x_i);
++i;
}
}
// `x_i' is going out of scope and so it will be destroyed.
// But `x_i' is sharing the implementation of a row in the matrix,
// so that its destruction will make the matrix inconsistent.
// To avoid this problem, assign a new and empty row to `x_i'.
Row null;
x_i.assign(null);
// Duplicate rows, that we have to erase, are those in between of
// indexes `new_last_row' and `last_row -1' (included).
rows.erase(rows.begin() + new_last_row, rows.begin() + last_row);
// NOTE: we cannot check for well-formedness of the matrix here,
// because the caller still has to update `index_first_pending'.
}
void
PPL::Matrix::sort_pending_and_remove_duplicates() {
assert(num_pending_rows() > 0);
assert(is_sorted());
Matrix& x = *this;
// The non-pending part of the matrix is already sorted.
// Now sorting the pending part..
const dimension_type first_pending = x.first_pending_row();
x.sort_rows(first_pending, x.num_rows());
// Recompute the number of rows, because we may have removed
// some rows occurring more than once in the pending part.
dimension_type num_rows = x.num_rows();
dimension_type k1 = 0;
dimension_type k2 = first_pending;
dimension_type num_duplicates = 0;
// In order to erase them, put at the end of the matrix
// those pending rows that also occur in the non-pending part.
while (k1 < first_pending && k2 < num_rows) {
const int cmp = compare(x[k1], x[k2]);
if (cmp == 0) {
// We found the same row.
++num_duplicates;
--num_rows;
// By initial sortedness, we can increment index `k1'.
++k1;
// Do not increment `k2'; instead, swap there the next pending row.
if (k2 < num_rows)
std::swap(x[k2], x[k2 + num_duplicates]);
}
else if (cmp < 0)
// By initial sortedness, we can increment `k1'.
++k1;
else {
// Here `cmp > 0'.
// Increment `k2' and, if we already found any duplicate,
// swap the next pending row in position `k2'.
++k2;
if (num_duplicates > 0 && k2 < num_rows)
std::swap(x[k2], x[k2 + num_duplicates]);
}
}
// If needed, swap any duplicates found past the pending rows
// that has not been considered yet; then erase the duplicates.
if (num_duplicates > 0) {
if (k2 < num_rows)
for (++k2; k2 < num_rows; ++k2)
std::swap(x[k2], x[k2 + num_duplicates]);
x.erase_to_end(num_rows);
}
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::add_row(const Row& row) {
// The added row must be strongly normalized and have
// the same number of elements of the existing rows of the matrix.
assert(row.check_strong_normalized());
assert(row.size() == row_size);
// This method is only used when the matrix has no pending rows.
assert(num_pending_rows() == 0);
const bool was_sorted = is_sorted();
const dimension_type new_rows_size = rows.size() + 1;
if (rows.capacity() < new_rows_size) {
// Reallocation will take place.
std::vector<Row> new_rows;
new_rows.reserve(compute_capacity(new_rows_size));
new_rows.insert(new_rows.end(), new_rows_size, Row());
// Put the new row in place.
Row new_row(row, row_capacity);
dimension_type i = new_rows_size-1;
std::swap(new_rows[i], new_row);
// Steal the old rows.
while (i-- > 0)
new_rows[i].swap(rows[i]);
// Put the new rows into place.
std::swap(rows, new_rows);
}
else {
// Reallocation will NOT take place.
// Inserts a new empty row at the end,
// then substitutes it with a copy of the given row.
Row tmp(row, row_capacity);
std::swap(*rows.insert(rows.end(), Row()), tmp);
}
// We update `index_first_pending', because it must
// equal to `num_rows()'.
set_index_first_pending_row(num_rows());
if (was_sorted) {
const dimension_type nrows = num_rows();
// The added row may have caused the matrix to be not sorted anymore.
if (nrows > 1) {
// If the matrix is not empty and the inserted row
// is the greatest one, the matrix is set to be sorted.
// If it is not the greatest one then the matrix is no longer sorted.
Matrix& x = *this;
set_sorted(x[nrows-2] <= x[nrows-1]);
}
else
// A matrix having only one row is sorted.
set_sorted(true);
}
// The added row was not a pending row.
assert(num_pending_rows() == 0);
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::add_pending_row(const Row& row) {
// The added row must be strongly normalized and have
// the same number of elements of the existing rows of the matrix.
assert(row.check_strong_normalized());
assert(row.size() == row_size);
const dimension_type new_rows_size = rows.size() + 1;
if (rows.capacity() < new_rows_size) {
// Reallocation will take place.
std::vector<Row> new_rows;
new_rows.reserve(compute_capacity(new_rows_size));
new_rows.insert(new_rows.end(), new_rows_size, Row());
// Put the new row in place.
Row new_row(row, row_capacity);
dimension_type i = new_rows_size-1;
std::swap(new_rows[i], new_row);
// Steal the old rows.
while (i-- > 0)
new_rows[i].swap(rows[i]);
// Put the new rows into place.
std::swap(rows, new_rows);
}
else {
// Reallocation will NOT take place.
// Inserts a new empty row at the end,
// then substitutes it with a copy of the given row.
Row tmp(row, row_capacity);
std::swap(*rows.insert(rows.end(), Row()), tmp);
}
// The added row was a pending row.
assert(num_pending_rows() > 0);
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::insert(const Row& row) {
// The added row must be strongly normalized and have
// the same topology of the matrix.
assert(row.check_strong_normalized());
assert(topology() == row.topology());
// This method is only used when the matrix has no pending rows.
assert(num_pending_rows() == 0);
const dimension_type old_num_rows = num_rows();
// Resize the matrix, if necessary.
if (row.size() > row_size) {
if (is_necessarily_closed() || old_num_rows == 0)
grow(old_num_rows, row.size());
else {
// After resizing, move the epsilon coefficients to
// the last column (note: sorting is preserved).
const dimension_type old_eps_index = row_size - 1;
grow(old_num_rows, row.size());
swap_columns(old_eps_index, row_size - 1);
}
add_row(row);
}
else if (row.size() < row_size)
if (is_necessarily_closed() || old_num_rows == 0)
add_row(Row(row, row_size, row_capacity));
else {
// Create a resized copy of the row (and move the epsilon
// coefficient to its last position).
Row tmp_row = Row(row, row_size, row_capacity);
std::swap(tmp_row[row.size() - 1], tmp_row[row_size - 1]);
add_row(tmp_row);
}
else
// Here row.size() == row_size.
add_row(row);
// The added row was not a pending row.
assert(num_pending_rows() == 0);
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::insert_pending(const Row& row) {
// The added row must be strongly normalized and have
// the same topology of the matrix.
assert(row.check_strong_normalized());
assert(topology() == row.topology());
const dimension_type old_num_rows = num_rows();
// Resize the matrix, if necessary.
if (row.size() > row_size) {
if (is_necessarily_closed() || old_num_rows == 0)
grow(old_num_rows, row.size());
else {
// After resizing, move the epsilon coefficients to
// the last column (note: sorting is preserved).
const dimension_type old_eps_index = row_size - 1;
grow(old_num_rows, row.size());
swap_columns(old_eps_index, row_size - 1);
}
add_pending_row(row);
}
else if (row.size() < row_size)
if (is_necessarily_closed() || old_num_rows == 0)
add_pending_row(Row(row, row_size, row_capacity));
else {
// Create a resized copy of the row (and move the epsilon
// coefficient to its last position).
Row tmp_row = Row(row, row_size, row_capacity);
std::swap(tmp_row[row.size() - 1], tmp_row[row_size - 1]);
add_pending_row(tmp_row);
}
else
// Here row.size() == row_size.
add_pending_row(row);
// The added row was a pending row.
assert(num_pending_rows() > 0);
// Do not check for strong normalization,
// because no modification of rows has occurred.
assert(OK(false));
}
void
PPL::Matrix::add_pending_row(const Row::Type type) {
const dimension_type new_rows_size = rows.size() + 1;
if (rows.capacity() < new_rows_size) {
// Reallocation will take place.
std::vector<Row> new_rows;
new_rows.reserve(compute_capacity(new_rows_size));
new_rows.insert(new_rows.end(), new_rows_size, Row());
// Put the new row in place.
Row new_row(type, row_size, row_capacity);
dimension_type i = new_rows_size-1;
std::swap(new_rows[i], new_row);
// Steal the old rows.
while (i-- > 0)
new_rows[i].swap(rows[i]);
// Put the new vector into place.
std::swap(rows, new_rows);
}
else
// Reallocation will NOT take place.
// Insert a new empty row at the end,
// then construct it assigning it the given type.
rows.insert(rows.end(), Row())->construct(type, row_size, row_capacity);
// The added row was a pending row.
assert(num_pending_rows() > 0);
}
void
PPL::Matrix::swap_columns(const dimension_type i, const dimension_type j) {
assert(i != j && i < num_columns() && j < num_columns());
for (dimension_type k = num_rows(); k-- > 0; ) {
Row& rows_k = rows[k];
std::swap(rows_k[i], rows_k[j]);
}
}
void
PPL::Matrix::permute_columns(const std::vector<dimension_type>& cycles) {
const dimension_type n = cycles.size();
for (dimension_type k = num_rows(); k-- > 0; ) {
Row& rows_k = rows[k];
for (dimension_type i = 0, j = 0; i < n; i = ++j) {
// Make `j' be the index of the next cycle terminator.
while (cycles[j] != 0)
++j;
// Cycles of length less than 2 are not allowed.
assert(j - i >= 2);
if (j - i == 2)
// For cycles of length 2 no temporary is needed, just a swap.
std::swap(rows_k[cycles[i]], rows_k[cycles[i+1]]);
else {
// Longer cycles need a temporary.
std::swap(rows_k[cycles[j-1]], tmp_Integer[0]);
for (dimension_type l = j-1; l > i; --l)
std::swap(rows_k[cycles[l-1]], rows_k[cycles[l]]);
std::swap(tmp_Integer[0], rows_k[cycles[i]]);
}
}
}
// The matrix may have lost sortedness.
set_sorted(false);
}
void
PPL::Matrix::normalize() {
// We normalize also the pending rows.
for (dimension_type i = num_rows(); i-- > 0; )
rows[i].normalize();
set_sorted(false);
}
void
PPL::Matrix::strong_normalize() {
// We strongly normalize also the pending rows.
for (dimension_type i = num_rows(); i-- > 0; )
rows[i].strong_normalize();
set_sorted(false);
}
/*! \relates Parma_Polyhedra_Library::Matrix */
bool
PPL::operator==(const Matrix& x, const Matrix& y) {
if (x.num_columns() != y.num_columns())
return false;
const dimension_type x_num_rows = x.num_rows();
const dimension_type y_num_rows = y.num_rows();
if (x_num_rows != y_num_rows)
return false;
if (x.first_pending_row() != y.first_pending_row())
return false;
for (dimension_type i = x_num_rows; i-- > 0; )
if (compare(x[i], y[i]) != 0)
return false;
return true;
}
void
PPL::Matrix::sort_and_remove_with_sat(SatMatrix& sat) {
Matrix& x = *this;
// We can only sort the non-pending part of the matrix.
dimension_type num_kept_rows = x.first_pending_row();
assert(num_kept_rows == sat.num_rows());
if (num_kept_rows <= 1) {
set_sorted(true);
return;
}
for (dimension_type i = 0; i < num_kept_rows - 1; ++i) {
for (dimension_type j = num_kept_rows - 1 ; j > i ; --j) {
const int cmp = compare(x[j], x[j - 1]);
if (cmp == 0) {
// If the compared rows are equals, we move the one with
// the greatest index (and the corresponding row of
// the saturation matrix) to the bottom of the matrix.
// Now the number of row is one less.
--num_kept_rows;
std::swap(x[j], x[num_kept_rows]);
std::swap(sat[j], sat[num_kept_rows]);
}
else if (cmp < 0) {
// If `x[j]' is less than `x[j - 1]' we swap the two rows
// and the corresponding rows of the saturation matrix.
std::swap(x[j], x[j - 1]);
std::swap(sat[j], sat[j - 1]);
}
}
}
if (num_pending_rows() > 0) {
// In this case, we must put the rows to erase after the
// pending rows.
const dimension_type num_rows_to_erase
= x.first_pending_row() - num_kept_rows;
const dimension_type n_rows = num_rows() - 1;
for (dimension_type i = 0; i < num_rows_to_erase; ++i)
std::swap(x[num_kept_rows + i], x[n_rows - i]);
}
// Erasing the duplicated rows...
x.erase_to_end(num_kept_rows + num_pending_rows());
x.set_index_first_pending_row(num_kept_rows);
// ... and the corresponding rows of the saturation matrix.
sat.rows_erase_to_end(num_kept_rows);
assert(check_sorted());
// Now the matrix is sorted.
x.set_sorted(true);
}
void
PPL::Matrix::gram_shmidt() {
assert(num_pending_rows() == 0);
// The first part of this algorithm is is an adaptation of the one
// proposed in a 1996 TR by Erlingsson, Kaltofen, and Musser
// "Generic Gram-Shmidt Orthogonalization by Exact Division".
// It is assumed that the lines/equalities come first in the matrix,
// which contains no redundant row.
const dimension_type rank = num_lines_or_equalities();
if (rank == 0)
return;
#if 0
std::cout << "+++ Before Gram-Shmidt +++" << std::endl;
ascii_dump(std::cout);
#endif
static std::vector<std::vector<Integer> > mu;
mu.reserve(compute_capacity(rank));
for (dimension_type i = mu.size(); i < rank; i++) {
std::vector<Integer> mu_i(i+1);
mu.push_back(mu_i);
}
// Compute the scalar products `rows[i]*rows[j]',
// for all 0 <= j <= i < rank, storing them into `mu[i][j]'.
for (dimension_type i = rank; i-- > 0; ) {
const Row& rows_i = rows[i];
std::vector<Integer>& mu_i = mu[i];
for (dimension_type j = i+1; j-- > 0; )
mu_i[j] = rows_i * rows[j];
}
const dimension_type n_columns = num_columns();
// Start from the second line/equality of the matrix.
for (dimension_type i = 1; i < rank; i++) {
Row& rows_i = rows[i];
std::vector<Integer>& mu_i = mu[i];
// Finish computing `mu[i][j]', for all j <= i.
for (dimension_type j = 0; j <= i; j++) {
const std::vector<Integer>& mu_j = mu[j];
if (j > 0)
mu_i[j] *= mu[j-1][j-1];
tmp_Integer[0] = 0;
for (dimension_type h = 0; h < j; h++) {
tmp_Integer[0] *= mu[h][h];
tmp_Integer[1] = mu_i[h] * mu_j[h];
tmp_Integer[0] += tmp_Integer[1];
if (h > 0)
exact_div_assign(tmp_Integer[0], mu[h-1][h-1]);
}
mu_i[j] -= tmp_Integer[0];
}
// Let the `i'-th line become orthogonal wrt the `j'-th line,
// for all 0 <= j < i.
for (dimension_type j = 0; j < i; j++) {
const Row& rows_j = rows[j];
const Integer& mu_ij = mu_i[j];
const Integer& mu_jj = mu[j][j];
for (dimension_type k = n_columns; k-- > 0; ) {
rows_i[k] *= mu_jj;
tmp_Integer[0] = mu_ij * rows_j[k];
rows_i[k] -= tmp_Integer[0];
if (j > 0)
exact_div_assign(rows_i[k], mu[j-1][j-1]);
}
}
}
// Normalize the coefficients of the orthogonal base found.
for (dimension_type i = rank; i-- > 0; )
rows[i].strong_normalize();
#if 0
std::cout << "+++ After Gram-Shmidt on the base +++" << std::endl;
ascii_dump(std::cout);
#endif
#ifndef NDEBUG
// Check that the new base is indeed orthogonal.
for (dimension_type i = rank; i-- > 0; ) {
const Row& rows_i = rows[i];
for (dimension_type j = i; j-- > 0; )
if (rows_i * rows[j] != 0) {
std::cout << "Not an orthogonal base" << std::endl;
std::cout << "i = " << i << ", j = " << j << std::endl;
std::cout << "After Gram-Shmidt on the base" << std::endl;
ascii_dump(std::cout);
assert(false);
}
}
#endif
// Let denominator = <v_0, v_0> * ... * <v_{rank-1}, v_{rank-1}>
// be the product of the squared norms of the orthogonal base.
// Define d[j] = denominator / <v_j, v_j>.
// Then, the formula to be computed, for each vector w which is not
// in the orthogonal base, is the following:
//
// w' = denominator * w - \sum_{j=0}^{rank-1} (d[j] * <w, v_j> * v_j)
//
// factors[j] will contain d[j] * <w, v_j>.
static std::vector<Integer> d;
static std::vector<Integer> factors;
d.reserve(compute_capacity(rank));
factors.reserve(compute_capacity(rank));
if (d.size() < rank) {
const dimension_type growth = rank - d.size();
d.insert(d.end(), growth, 0);
factors.insert(factors.end(), growth, 0);
}
// Computing all the factors d[0], ..., d[rank-1], and the denominator.
Integer denominator = 1;
for (dimension_type i = rank; i-- > 0; ) {
const Row& rows_i = rows[i];
d[i] = rows_i * rows_i;
denominator *= d[i];
}
for (dimension_type i = rank; i-- > 0; )
exact_div_assign(d[i], denominator, d[i]);
// Orthogonalize the rows that are not lines/equalities.
const dimension_type n_rows = num_rows();
for (dimension_type i = rank; i < n_rows; i++) {
Row& w = rows[i];
// Compute `factors' according to `w'.
for (dimension_type j = rank; j-- > 0; ) {
factors[j] = w * rows[j];
factors[j] *= d[j];
}
for (dimension_type k = n_columns; k-- > 0; )
w[k] *= denominator;
for (dimension_type j = rank; j-- > 0; ) {
const Row& v_j = rows[j];
for (dimension_type k = n_columns; k-- > 0; )
w[k] -= factors[j] * v_j[k];
}
assert(w.is_ray_or_point_or_inequality());
w.normalize();
#if 0
std::cout << "+++ After Gram-Shmidt on the whole matrix +++" << std::endl;
ascii_dump(std::cout);
#endif
#ifndef NDEBUG
// Check that w is indeed orthogonal wrt all the vectors in the base.
for (dimension_type h = rank; h-- > 0; )
if (w * rows[h] != 0) {
std::cout << "Not orthogonal" << std::endl;
std::cout << "i = " << i << ", h = " << h << std::endl;
std::cout << "After Gram-Shmidt on the whole matrix" << std::endl;
ascii_dump(std::cout);
assert(false);
}
#endif
}
// Matrix may be no longer sorted (unless it has one line/equality
// and at most one ray/point/inequality).
if (rank > 1 || n_rows > rank + 1)
set_sorted(false);
// A well-formed matrix has to be returned.
assert(OK(true));
}
PPL::dimension_type
PPL::Matrix::gauss() {
// This method is only applied to a well-formed matrix
// having no pending rows.
assert(OK(true));
assert(num_pending_rows() == 0);
dimension_type rank = 0;
// Will keep track of the variations on the matrix of equalities.
bool changed = false;
const dimension_type n_columns = num_columns();
const dimension_type n_lines_or_equalities = num_lines_or_equalities();
for (dimension_type j = n_columns; j-- > 0; ) {
for (dimension_type i = rank; i < n_lines_or_equalities; ++i) {
// Looking for the first non-zero coefficient (the pivot)
// in the j-th column, starting from the last column.
if (rows[i][j] != 0) {
// We want the pivot to be placed on the secondary diagonal,
// if it is not the case, we swap the row containing it
// with the one indexed by rank (that can be a previous one
// or the same: in this case we do not swap).
if (i > rank) {
std::swap(rows[i], rows[rank]);
// After swapping the matrix is no longer sorted.
changed = true;
}
// Linear combining the row containing the pivot with
// all the ones that follow it such that all the elements
// on the j-th column (of these rows) become 0.
for (dimension_type k = i + 1; k < n_lines_or_equalities; ++k) {
if (rows[k][j] != 0) {
rows[k].linear_combine(rows[rank], j);
changed = true;
}
}
// Have to consider the rows following the rank-th one
// because until that one are already triangularized.
++rank;
break;
}
}
}
if (changed)
set_sorted(false);
// A well-formed matrix is returned.
assert(OK(true));
return rank;
}
void
PPL::Matrix::back_substitute(const dimension_type rank) {
// This method is only applied to a well-formed matrix
// having no pending rows.
assert(OK(true));
assert(num_pending_rows() == 0);
// The matrix describes a non-empty polyhedron and thus it always
// contains a row which is not a line/equality (corresponding to
// a vertex or to a low-level constraint).
assert(num_rows() > rank);
const dimension_type nrows = num_rows();
// Trying to keep sortedness.
bool still_sorted = is_sorted();
// This deque of booleans will be used to flag those rows that,
// before exiting, need to be re-checked for sortedness.
std::deque<bool> check_for_sortedness;
if (still_sorted)
check_for_sortedness.insert(check_for_sortedness.end(), nrows, false);
for (dimension_type k = rank; k-- > 0; ) {
// For each row, starting from the rank-th one,
// looks for the last non-zero element.
// j will be the index of such a element.
Row& rows_k = rows[k];
dimension_type j = num_columns() - 1;
while (j != 0 && rows_k[j] == 0)
--j;
// Go through the equalities above `rows_k'.
for (dimension_type i = k; i-- > 0; ) {
Row& rows_i = rows[i];
assert(rows_i.is_line_or_equality());
if (rows_i[j] != 0) {
// Combine linearly `rows_i' with `rows_k'
// so that `rows_i[j]' becomes zero.
rows_i.linear_combine(rows_k, j);
if (still_sorted) {
// Trying to keep sortedness: remember which rows
// have to be re-checked for sortedness at the end.
if (i > 0)
check_for_sortedness[i-1] = true;
check_for_sortedness[i] = true;
}
}
}
// Due to strong normalization during previous iterations,
// the pivot coefficient `rows_k[j]' may now be negative.
// Since an inequality (or ray or point) cannot be multiplied
// by a negative factor, the coefficient of the pivot must be
// forced to be positive.
const bool have_to_negate = (rows_k[j] < 0);
if (have_to_negate)
for (dimension_type h = num_columns(); h-- > 0; )
PPL::negate(rows_k[h]);
// Note: we do not mark index `k' in `check_for_sortedness',
// because we will later negate back the row.
// Go through all the inequalities of the matrix.
for (dimension_type i = rank; i < nrows; ++i) {
Row& rows_i = rows[i];
if (rows_i[j] != 0) {
// Combine linearly the `rows_i' with `rows_k'
// so that `rows_i[j]' becomes zero.
rows_i.linear_combine(rows_k, j);
if (still_sorted) {
// Trying to keep sortedness: remember which rows
// have to be re-checked for sortedness at the end.
if (i > rank)
check_for_sortedness[i-1] = true;
check_for_sortedness[i] = true;
}
}
}
if (have_to_negate)
// Negate `rows_k' to restore strong-normalization.
for (dimension_type h = num_columns(); h-- > 0; )
PPL::negate(rows_k[h]);
}
// Trying to keep sortedness.
for (dimension_type i = 0, iend = nrows-1; still_sorted && i < iend; ++i)
if (check_for_sortedness[i])
// Have to check sortedness of `mat[i]' wrt `mat[i+1]'.
still_sorted = (rows[i] <= rows[i+1]);
// Set the sortedness flag.
set_sorted(still_sorted);
// A well-formed matrix is returned.
assert(OK(true));
}
void
PPL::Matrix::add_rows_and_columns(const dimension_type n) {
assert(n > 0);
const bool was_sorted = is_sorted();
const dimension_type old_n_rows = num_rows();
const dimension_type old_n_columns = num_columns();
grow(old_n_rows + n, old_n_columns + n);
Matrix& x = *this;
// The old matrix is moved to the bottom.
for (dimension_type i = old_n_rows; i-- > 0; )
std::swap(x[i], x[i + n]);
for (dimension_type i = n, c = old_n_columns; i-- > 0; ) {
// The top right-hand sub-matrix (i.e., the matrix made
// of new rows and columns) is set to the specular image
// of the identity matrix.
Row& r = x[i];
r[c++] = 1;
r.set_is_line_or_equality();
// Note: `r' is strongly normalized.
}
// If the old matrix was empty, the last row added is either
// a positivity constraint or a point.
if (old_n_columns == 0) {
x[n-1].set_is_ray_or_point_or_inequality();
// Since ray, points and inequalities come after lines
// and equalities, this case implies the matrix is sorted.
set_sorted(true);
}
else if (was_sorted)
set_sorted(x[n-1] <= x[n]);
// A well-formed matrix has to be returned.
assert(OK(true));
}
bool
PPL::Matrix::check_sorted() const {
const Matrix& x = *this;
for (dimension_type i = first_pending_row(); i-- > 1; )
if (x[i] < x[i-1])
return false;
return true;
}
bool
PPL::Matrix::OK(const bool check_strong_normalized) const {
#ifndef NDEBUG
using std::endl;
using std::cerr;
#endif
// `index_first_pending' must be less then or equal to `num_rows()'.
if (first_pending_row() > num_rows()) {
#ifndef NDEBUG
cerr << "Matrix has a negative number of pending rows!"
<< endl;
#endif
return false;
}
// An empty matrix must have num_columns() == 0.
if (num_rows() == 0)
if (num_columns() == 0)
// An empty matrix is OK.
return true;
else {
#ifndef NDEBUG
cerr << "Matrix has no rows but num_columns() is nonzero!"
<< endl;
#endif
return false;
}
// A non-empty matrix will contain constraints or generators; in
// both cases it must have at least one column for the inhomogeneous
// term and, if it is non-necessarily closed, another one
// for the epsilon coefficient.
const dimension_type min_cols = is_necessarily_closed() ? 1 : 2;
if (num_columns() < min_cols) {
#ifndef NDEBUG
cerr << "Matrix has fewer columns than the minimum "
<< "allowed by its topology:"
<< endl
<< "num_columns is " << num_columns()
<< ", minimum is " << min_cols
<< endl;
#endif
return false;
}
const Matrix& x = *this;
const dimension_type n_rows = num_rows();
for (dimension_type i = 0; i < n_rows; ++i) {
if (!x[i].OK(row_size, row_capacity))
return false;
// Checking for topology mismatches.
if (x.topology() != x[i].topology()) {
#ifndef NDEBUG
cerr << "Topology mismatch between the matrix "
<< "and one of its rows!"
<< endl;
#endif
return false;
}
}
if (check_strong_normalized) {
// Check for strong normalization of rows.
// Note: normalization cannot be checked inside the Row::OK() method,
// because a Row object may also implement a LinExpression object,
// which in general cannot be (strongly) normalized.
Matrix tmp = x;
tmp.strong_normalize();
if (x != tmp) {
#ifndef NDEBUG
cerr << "Matrix rows are not strongly normalized!"
<< endl;
#endif
return false;
}
}
if (sorted && !check_sorted()) {
#ifndef NDEBUG
cerr << "The matrix declares itself to be sorted but it is not!"
<< endl;
#endif
return false;
}
// All checks passed.
return true;
}
syntax highlighted by Code2HTML, v. 0.9.1