smatrix.cc
来自「用于计算矩阵的特征值,及矩阵的其他运算.可以用与稀疏矩阵」· CC 代码 · 共 457 行
CC
457 行
// Copyright (C) 2002 Charless C. Fowlkes <fowlkes@eecs.berkeley.edu>
// Copyright (C) 2002 David R. Martin <dmartin@eecs.berkeley.edu>
//
// This program 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.
//
// This program 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, or see http://www.gnu.org/copyleft/gpl.html.
#include <iostream>
#include <stdio.h>
#include <math.h>
#include "smatrix.hh"
#include "util.hh"
#include "string.hh"
#include "exception.hh"
#include "message.hh"
SMatrix::SMatrix (int n, int* nz, int** col, double** values)
{
this->n = n;
this->nz = nz;
this->col = col;
this->values = values;
int nnz = 0;
for (int i = 0; i < n; i++)
nnz += nz[i];
Util::Message::debug(Util::String("creating sparse matrix with %d nonzero entries",nnz));
}
SMatrix::SMatrix(FILE* fp)
{
fread(&n,sizeof(int),1,fp);
int nnz;
fread(&nnz,sizeof(int),1,fp);
nz = new int[n];
col = new int*[n];
values = new double*[n];
for (int row = 0; row < n; row++) {
fread(&nz[row],sizeof(int),1,fp);
col[row] = new int[nz[row]];
values[row] = new double[nz[row]];
fread(values[row],sizeof(double),nz[row],fp);
fread(col[row],sizeof(int),nz[row],fp);
}
}
SMatrix::~SMatrix ()
{
for (int i = 0; i < n; i++)
{
delete[] col[i];
delete[] values[i];
}
delete col;
delete values;
delete nz;
}
void
SMatrix::dump(FILE* fp)
{
fwrite(&n,sizeof(int),1,fp);
int nnz = getNNZ();
fwrite(&nnz,sizeof(int),1,fp);
for (int row = 0; row < n; row++) {
fwrite(&nz[row],sizeof(int),1,fp);
fwrite(values[row],sizeof(double),nz[row],fp);
fwrite(col[row],sizeof(int),nz[row],fp);
}
}
int SMatrix::getNNZ() const
{
int nnz = 0;
for (int i = 0; i < n; i++)
nnz += nz[i];
return nnz;
}
//older, slower symmetrize
/*
void SMatrix::symmetrize()
{
for (int r = 0; r < n; r++)
{
for (int i = 0; i < nz[r]; i++)
{
int c = col[r][i];
int j = -1;
for (int k = 0; k < nz[c]; k++)
{
if (col[c][k] == r)
{
j = k;
}
}
assert(j != -1);
double v_rc = values[r][i];
double v_cr = values[c][j];
values[r][i] = 0.5*(v_rc+v_cr);
values[c][j] = 0.5*(v_rc+v_cr);
}
}
}
*/
void SMatrix::symmetrize()
{
int* tail = new int[n];
memset(tail,0,n*sizeof(int));
for (int r = 0; r < n; r++)
{
int offset = 0;
while ((offset < nz[r]) && (col[r][offset] < r+1))
{
offset++;
}
for (int i = offset; i < nz[r]; i++)
{
int c = col[r][i];
assert( col[c][tail[c]] == r );
double v_rc = values[r][i];
double v_cr = values[c][tail[c]];
values[r][i] = 0.5*(v_rc+v_cr);
values[c][tail[c]] = 0.5*(v_rc+v_cr);
tail[c]++;
}
}
}
double*
SMatrix::getRowSum () const
{
double* rowSum = new double[n];
memset(rowSum,0,n*sizeof(double));
Util::Message::debug("computing row sum");
for (int row = 0; row < n; row++) {
double sum = 0;;
for (int j = 0; j < nz[row]; j++) {
sum += values[row][j];
}
rowSum[row] = sum;
}
return rowSum;
}
/*
double
SMatrix::computeNCut(const double* rowSum, const int* membership, const int nsegs) const
{
if (nsegs > 1)
{
double* num = new double[nsegs];
double* den = new double[nsegs];
memset(num,0,nsegs*sizeof(double));
memset(den,0,nsegs*sizeof(double));
for (int row = 0; row < n; row++)
{
int segi = membership[row];
den[segi] += rowSum[row];
for (int j = 0; j < nz[row]; j++)
{
int segj = membership[col[row][j]];
if (segi == segj)
{
num[segi] += values[row][j];
}
}
}
double assoc = 0;
for (int s = 0; s < nsegs; s++)
{
if (den[s] > 0)
{
assoc += (num[s] / den[s]);
}
}
return (1 - ((1/(double)nsegs)*assoc));
}
else
{
std::cerr << "only 1 segment" << std::endl;
return 0;
}
}
*/
double
SMatrix::computeNCut(const double* rowSum, const Util::Array1D<int> membership, const int nsegs) const
{
if (nsegs > 1)
{
double* num = new double[nsegs];
double* den = new double[nsegs];
memset(num,0,nsegs*sizeof(double));
memset(den,0,nsegs*sizeof(double));
for (int row = 0; row < n; row++)
{
int segi = membership(row);
den[segi] += rowSum[row];
for (int j = 0; j < nz[row]; j++)
{
int segj = membership(col[row][j]);
if (segi == segj)
{
num[segi] += values[row][j];
}
}
}
double assoc = 0;
for (int s = 0; s < nsegs; s++)
{
if (den[s] > 0)
{
assoc += (num[s] / den[s]);
}
}
delete num;
delete den;
return (1 - ((1/(double)nsegs)*assoc));
}
else
{
Util::Message::debug("only 1 segment!!");
return 0;
}
}
void
SMatrix::normalizedLaplacian(const double* rowSum)
{
double* isrd = new double[n];
for (int i = 0; i < n; i++) {
isrd[i] = 1.0 / sqrt(rowSum[i]);
}
Util::Message::debug("scaling rows");
for (int row = 0; row < n; row++) {
double isrdrow = isrd[row];
for (int j = 0; j < nz[row]; j++) {
values[row][j] = isrdrow * values[row][j] * isrd[col[row][j]];
}
}
delete[] isrd;
}
void
SMatrix::undoNormalizedLaplacian(const double* rowSum)
{
double* isrd = new double[n];
for (int i = 0; i < n; i++) {
isrd[i] = sqrt(rowSum[i]);
}
Util::Message::debug("scaling rows");
for (int row = 0; row < n; row++) {
double isrdrow = isrd[row];
for (int j = 0; j < nz[row]; j++) {
values[row][j] = isrdrow * values[row][j] * isrd[col[row][j]];
}
}
delete[] isrd;
}
void
SMatrix::mvmul (const double* a, double* b) const
{
for (int row = 0; row < n; row++) {
double bval = 0;
for (int j = 0; j < nz[row]; j++) {
bval += a[col[row][j]] * values[row][j];
}
b[row] = bval;
}
}
void
SMatrix::mvmul (const double* a1, const double* a2,
double* b1, double* b2) const
{
for (int row = 0; row < n; row++) {
double bval1 = 0;
double bval2 = 0;
for (int j = 0; j < nz[row]; j++) {
double v = values[row][j];
bval1 += a1[col[row][j]] * v;
bval2 += a2[col[row][j]] * v;
}
b1[row] = bval1;
b2[row] = bval2;
}
}
void
SMatrix::mvmul (const double* a1, const double* a2,
const double* a3, const double* a4,
double* b1, double* b2,
double* b3, double* b4) const
{
for (int row = 0; row < n; row++) {
double bval1 = 0;
double bval2 = 0;
double bval3 = 0;
double bval4 = 0;
for (int j = 0; j < nz[row]; j++) {
double v = values[row][j];
bval1 += a1[col[row][j]] * v;
bval2 += a2[col[row][j]] * v;
bval3 += a3[col[row][j]] * v;
bval4 += a4[col[row][j]] * v;
}
b1[row] = bval1;
b2[row] = bval2;
b3[row] = bval3;
b4[row] = bval4;
}
}
typedef void (*OpFunc) (int* nrow, int* ncol, double* xin,
int* ldx, double* yout, int* ldy);
extern "C" void trlan77_ (OpFunc op, int ipar[32], int* nrow1, int* mev,
double* eval, double* evec,
int* nrow2, double* wrk, int* lwrk);
static SMatrix* opMatrix = NULL;
static void
op (int* nrow, int* ncol, double* xin, int* ldx, double* yout, int* ldy)
{
Util::Message::stepBlock();
assert (opMatrix != NULL);
assert (*nrow == (int)opMatrix->n);
int col = 0;
for (; col < *ncol - 3; col += 4) {
opMatrix->mvmul (&xin[col*(*ldx)], &xin[(col+1)*(*ldx)], &xin[(col+2)*(*ldx)], &xin[(col+3)*(*ldx)],
&yout[col*(*ldy)], &yout[(col+1)*(*ldy)], &yout[(col+2)*(*ldy)], &yout[(col+3)*(*ldy)]);
}
for (; col < *ncol - 1; col += 2) {
opMatrix->mvmul (&xin[col*(*ldx)], &xin[(col+1)*(*ldx)], &yout[col*(*ldy)], &yout[(col+1)*(*ldy)]);
}
for (; col < *ncol; col++) {
opMatrix->mvmul (&xin[col*(*ldx)], &yout[col*(*ldy)]);
}
}
void
SMatrix::eigs(int nvectors, double** eval, double** evec)
{
opMatrix = this;
int stat = 0; // Error status.
int nrow = this->n; // Matrix size.
int lohi = 1; // -1 == small , 1 == large eigenvalues?
int ned = nvectors; // Number of eigenpairs to compute.
int maxlan = 0; // Maximum Lanczos basis size, set below.
int mev = ned; // Number of eigenpairs that can be returned.
int maxmv = 1000*nvectors; // Max number of MV muls allowed.
int restart = 1; // Restarting scheme.
int nec = 0; // Number of eigenpairs already converged.
int iguess = -1; // Initial guess (0=all ones; -1=ones + perturb
int cpflag = 0; // Checkpoint flag.
int cpio = 98; // Fortran IO unit number for checkpoint files.
int mpicom = 0; // MPI communicator.
int logio = 99; // Fortran IO unit number for debug messages.
int verbose = 0; // Verbose level.
double tol = 1e-8; // Relative tolerance on residual norms.
int mvop = 2*this->getNNZ(); // FLOPs per MV mul.
int ipar[32] = {stat, lohi, ned, nec, maxlan, restart, maxmv, mpicom,
verbose, logio, iguess, cpflag, cpio, mvop};
// Allocate space for computed eigenvalues.
*eval = new double[mev];
memset(*eval,0,mev*sizeof(double));
// Allocate space for computed eigenvectors.
*evec = new double [nrow * mev];
memset(*evec,1,nrow*mev*sizeof(double));
// Allocate workspace for TRLan. Rule of thumb given
// in Section 6.1 of TRLan manual for minimum size of maxlan.
maxlan = Util::max (maxlan, ned + Util::min (6, ned));
int lwrk = maxlan * (maxlan + 10);
double* wrk = new double [lwrk];
// Put tolerance at beginning of workspace!
wrk[0] = tol;
Util::Message::startBlock(maxmv,"trlan");
trlan77_ (op, ipar, &nrow, &mev, *eval, *evec, &nrow, wrk, &lwrk);
Util::Message::endBlock();
// cleanup.
delete [] wrk;
stat = ipar[0];
nec = ipar[3];
if (stat == 0 && nec >= ned)
{
std::cerr << " trlan exited normally" << std::endl;
}
else
{
throw Util::Exception (Util::String (
"TRLan error code %d. See the TRLan User Guide for a "
"description of this error condition and tips for how to "
"fix it.\n", stat));
}
//reverse ordering of the eigenvectors so the
//first eigenvector has the largest eigenvalue
//and make them unit norm
double* teval = new double[mev];
double* tevec = new double[nrow*mev];
for (int i = 0; i < mev; i++)
{
teval[i] = (*eval)[mev-i-1];
float norm = 0.0f;
for (int j = 0; j < nrow; j++)
{
tevec[i*nrow+j] = (*evec)[(mev-i-1)*nrow+j];
norm += tevec[i*nrow+j]*tevec[i*nrow+j];
}
norm = sqrt(norm);
for (int j = 0; j < nrow; j++)
{
tevec[i*nrow+j] /= norm;
}
}
delete *evec;
delete *eval;
*evec = tevec;
*eval = teval;
std::cerr << " smallest eigenvalue: " << (*eval)[mev-1] << std::endl;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?