Files
mlpack/fastlib/u/nvasil/tpie/ami_sparse_matrix.h
T

377 lines
10 KiB
C++

// Copyright (c) 1995 Darren Vengroff
//
// File: ami_sparse_matrix.h
// Author: Darren Vengroff <darrenv@eecs.umich.edu>
// Created: 3/2/95
//
// $Id: ami_sparse_matrix.h,v 1.14 2005/07/07 20:45:37 adanner Exp $
//
#ifndef AMI_SPARSE_MATRIX_H
#define AMI_SPARSE_MATRIX_H
// Get definitions for working with Unix and Windows
#include "u/nvasil/tpie/portability.h"
#include <iostream>
// We need dense matrices to support some sparse/dense interactions.
#include "u/nvasil/tpie/ami_matrix.h"
// A spares matrix element is labeled with a row er and a column ec.
// A sparse matrix is simply represented by a colletion of these.
template <class T>
class AMI_sm_elem {
public:
TPIE_OS_OFFSET er;
TPIE_OS_OFFSET ec;
T val;
};
template <class T>
ostream &operator<<(ostream& s, const AMI_sm_elem<T> &a)
{
return s << a.er << ' ' << a.ec << ' ' << a.val;
};
template <class T>
istream &operator>>(istream& s, AMI_sm_elem<T> &a)
{
return s >> a.er >> a.ec >> a.val;
};
template<class T>
class AMI_sparse_matrix : public AMI_STREAM< AMI_sm_elem<T> > {
private:
// How many rows and columns.
TPIE_OS_OFFSET r,c;
public:
AMI_sparse_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col);
~AMI_sparse_matrix(void);
TPIE_OS_OFFSET rows();
TPIE_OS_OFFSET cols();
};
template<class T>
AMI_sparse_matrix<T>::AMI_sparse_matrix(TPIE_OS_OFFSET row, TPIE_OS_OFFSET col) :
r(row), c(col), AMI_STREAM< AMI_sm_elem<T> >()
{
}
template<class T>
AMI_sparse_matrix<T>::~AMI_sparse_matrix(void)
{
}
template<class T>
TPIE_OS_OFFSET AMI_sparse_matrix<T>::rows(void)
{
return r;
}
template<class T>
TPIE_OS_OFFSET AMI_sparse_matrix<T>::cols(void)
{
return c;
}
//
// A class of comparison object designed to facilitate sorting of
// elements of the spase matrix into bands.
//
template<class T>
class sm_band_comparator
{
private:
TPIE_OS_SIZE_T rpb;
public:
sm_band_comparator(TPIE_OS_SIZE_T rows_per_band) :
rpb(rows_per_band) {};
virtual ~sm_band_comparator(void) {};
// If they are in the same band, compare columns, otherwise,
// compare rows.
int compare(const AMI_sm_elem<T> &t1, const AMI_sm_elem<T> &t2) {
if ((t1.er / rpb) == (t2.er / rpb)) {
return int(t1.ec) - int(t2.ec);
} else {
return int(t1.er) - int(t2.er);
}
}
};
// A function to bandify a sparse matrix.
template<class T>
AMI_err AMI_sparse_bandify(AMI_sparse_matrix<T> &sm,
AMI_sparse_matrix<T> &bsm,
TPIE_OS_SIZE_T rows_per_band)
{
AMI_err ae;
sm_band_comparator<T> cmp(rows_per_band);
ae = AMI_sort_V1((AMI_STREAM< AMI_sm_elem<T> > *)&sm,
(AMI_STREAM< AMI_sm_elem<T> > *)&bsm,
(sm_band_comparator<T> *)&cmp);
return ae;
}
// Get all band information for the given matrix and the current
// runtime environment.
template<class T>
AMI_err AMI_sparse_band_info(AMI_sparse_matrix<T> &opm,
TPIE_OS_SIZE_T &rows_per_band,
TPIE_OS_OFFSET &total_bands)
{
TPIE_OS_SIZE_T sz_avail, single_stream_usage;
TPIE_OS_OFFSET rows = opm.rows();
AMI_err ae;
// Check available main memory.
sz_avail = MM_manager.memory_available ();
// How much memory does a single stream need in the worst case?
if ((ae = opm.main_memory_usage(&single_stream_usage,
MM_STREAM_USAGE_MAXIMUM)) !=
AMI_ERROR_NO_ERROR) {
return ae;
}
// Figure out how many elements of the output can fit in main
// memory at a time. This will determine the number of rows of
// the sparse matrix that go into a band.
rows_per_band = (sz_avail - single_stream_usage * 5) / sizeof(T);
if (rows_per_band > rows) {
rows_per_band = (TPIE_OS_SIZE_T)rows;
}
total_bands = (rows + rows_per_band - 1) / rows_per_band;
return AMI_ERROR_NO_ERROR;
}
//
//
//
//
template<class T>
AMI_err AMI_sparse_mult_scan_banded(AMI_sparse_matrix<T> &banded_opm,
AMI_matrix<T> &opv, AMI_matrix<T> &res,
TPIE_OS_OFFSET rows, TPIE_OS_OFFSET /*cols*/,
TPIE_OS_SIZE_T rows_per_band)
{
AMI_err ae;
AMI_sm_elem<T> *sparse_current;
T *vec_current;
TPIE_OS_OFFSET vec_row;
banded_opm.seek(0);
ae = banded_opm.read_item(&sparse_current);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
opv.seek(0);
ae = opv.read_item(&vec_current);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
vec_row = 0;
res.seek(0);
TPIE_OS_OFFSET next_band_start = rows_per_band;
TPIE_OS_OFFSET ii;
TPIE_OS_OFFSET curr_band_start = 0;
TPIE_OS_SIZE_T rows_in_current_band = rows_per_band;
bool sparse_done = false;
T *output_subvector = new T[rows_per_band];
for (ii = rows_per_band; ii--; ) {
output_subvector[ii] = 0;
}
while (1) {
//
// Each time we enter this loop, we have the following invariants:
//
// vec_current = an element of the vector.
//
// vec_row = row vec_current came from.
//
// sparse_current = current element from the banded sparse mat.
//
// curr_band_start = row beginning current band.
//
// next_band_start = row beginning next band.
//
// rows_in_current_band = as name implies.
//
if (sparse_done || (sparse_current->er >= next_band_start)) {
// If we are out of sparse elements or the sparse element
// row is in the next band then we have to write the
// current results, reset the output buffer, and rewind
// the vector.
ae = res.write_array(output_subvector, rows_in_current_band);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
if (sparse_done) {
// Write more zeores if necessary before breaking out
// of the loop. We have to do this in some cases if
// there are one or more empty bands at the bottom of
// the sparse matrix. It is unlikely this sort of
// thing would ever really happen, but we should be
// careful anyway.
T tmp = 0;
for (ii = rows - next_band_start; ii--; ) {
ae = res.write_item(tmp);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
}
break;
}
for (ii = rows_in_current_band; ii--; ) {
output_subvector[ii] = 0;
}
opv.seek(0);
curr_band_start = next_band_start;
next_band_start += rows_per_band;
if (next_band_start > rows) {
// The final band may not have exactly rows_per_band
// rows due to roundoff. We make the appropropriate
// adjustments here.
rows_in_current_band = (TPIE_OS_SIZE_T)(rows - curr_band_start);
next_band_start = rows;
} else {
rows_in_current_band = rows_per_band;
}
} else if (sparse_current->ec == vec_row) {
// If the column of the sparse matrix and the row of the
// vector that the current inputs come from are the same,
// then multiply them, add the result to the appropriate
// output element, and advance past the sparse element.
output_subvector[sparse_current->er - curr_band_start] +=
sparse_current->val * *vec_current;
ae = banded_opm.read_item(&sparse_current);
if (ae == AMI_ERROR_END_OF_STREAM) {
sparse_done = true;
} else if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
} else {
// If the sparse element column is past the current
// row in the vector, then advance the vector.
tp_assert(sparse_current->ec > vec_row,
"Sparse column fell behind current row.");
ae = opv.read_item(&vec_current);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
vec_row++;
}
}
delete [] output_subvector;
return AMI_ERROR_NO_ERROR;
}
// Multiply a sparse (n,m)-matrix by a dense m-vector to get a dense
// n-vector.
template<class T>
AMI_err AMI_sparse_mult(AMI_sparse_matrix<T> &opm, AMI_matrix<T> &opv,
AMI_matrix<T> &res)
{
TPIE_OS_SIZE_T rows_per_band;
TPIE_OS_OFFSET total_bands;
TPIE_OS_OFFSET rows;
TPIE_OS_OFFSET cols;
// size_t sz_avail, single_stream_usage;
AMI_err ae;
// Make sure the sizes of the matrix and vectors match up.
rows = opm.rows();
cols = opm.cols();
if ((cols != opv.rows()) || (rows != res.rows()) ||
(opv.cols() != 1) || (res.cols() != 1)) {
return AMI_MATRIX_BOUNDS;
}
// Get band information.
ae = AMI_sparse_band_info(opm, rows_per_band, total_bands);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Partition the sparse matrix into bands with the elements within
// a band sorted by column. This is all done by a single sort
// operation.
//
// Note that if our goal is to multiply a large number of
// different vectors by a single matrix we should seperate this
// step out into a preprocessing phacse so that the sort is only
// done once.
AMI_sparse_matrix<T> banded_opm(rows, cols);
ae = AMI_sparse_bandify(opm, banded_opm, rows_per_band);
if (ae != AMI_ERROR_NO_ERROR) {
return ae;
}
// Scan the contents of the bands and the vector to produce output.
ae = AMI_sparse_mult_scan_banded(banded_opm, opv, res,
rows, cols, rows_per_band);
return ae;
}
#endif // _AMI_SPARSE_MATRIX_H