From 0039b2722c843ad4bb9a499aa0d4861076ae60c1 Mon Sep 17 00:00:00 2001 From: Jesper Andersson <jesper@fmrib.ox.ac.uk> Date: Tue, 19 Mar 2019 17:06:35 +0000 Subject: [PATCH] Implements generation of specialised sparse matrices --- SpMatMatrices.cpp | 263 ++++++++++++++++++++++++++++++++++++++++++++++ SpMatMatrices.h | 36 +++++++ 2 files changed, 299 insertions(+) create mode 100644 SpMatMatrices.cpp create mode 100644 SpMatMatrices.h diff --git a/SpMatMatrices.cpp b/SpMatMatrices.cpp new file mode 100644 index 0000000..5e99017 --- /dev/null +++ b/SpMatMatrices.cpp @@ -0,0 +1,263 @@ +// +// Declarations for functions generating specialised +// sparse matrices of type SpMat +// +// SpMatMatrices.h +// +// Declares global functions for generating specialised +// sparse matrices of type SpMat +// +// Jesper Andersson, FMRIB Image Analysis Group +// +// Copyright (C) 2019 University of Oxford +// +/* CCOPYRIGHT */ + +#include <vector> +#include "newmat.h" +#include "SpMat.h" +#include "SpMatMatrices.h" + +/*! + * Global function that creates and returns a symmetric + * Toeplitz matrix with dimensions col.Nrows() x col.Nrows() and where the + * first column is given by col and all subsequent columns are translated + * and shifted versions of that column. + * \return A sparse symmetric Toeplitz matrix + * \param[in] col First column of matrix + */ +MISCMATHS::SpMat<float> SparseSymmetricToeplitz(const NEWMAT::ColumnVector& col) +{ + unsigned int mn = static_cast<unsigned int>(col.Nrows()); + unsigned int nnz = 0; // No of non-zeros per column + for (unsigned int i=0; i<mn; i++) nnz += (col(i+1) == 0) ? 0 : 1; + std::vector<unsigned int> indx(nnz); + std::vector<float> val(nnz); + { + unsigned int i = 0; unsigned int j = 0; + for (i=0, j=0; i<mn; i++) if (col(i+1) != 0) { indx[j] = i; val[j++] = static_cast<float>(col(i+1)); } + } + unsigned int *irp = new unsigned int[nnz*mn]; + unsigned int *jcp = new unsigned int[mn+1]; + double *sp = new double[nnz*mn]; + unsigned int irp_cntr = 0; + for (unsigned int col=0; col<mn; col++) { + jcp[col] = irp_cntr; + for (unsigned int r=0; r<nnz; r++) { + irp[irp_cntr] = indx[r]; + sp[irp_cntr++] = val[r]; + indx[r] = (indx[r] == mn-1) ? 0 : indx[r]+1; + } + } + jcp[mn] = irp_cntr; + MISCMATHS::SpMat<float> tpmat(mn,mn,irp,jcp,sp); + delete [] irp; delete [] jcp; delete [] sp; + return(tpmat); +} + +/*! + * Global function that creates and returns a symmetric matrix with dimensions + * prod(isz) x prod(isz) and which represent an approximate Hessian for + * Bending energy. It is approximate because it only considers the straight + * second derivatives. + * \return A sparse symmetric Hessian of Bending Energy + * \param[in] isz 3 element vector specifying matrix size of image + * \param[in] vxs 3 element vector with voxel size in mm + * \param[in] bc Boundary condition (PERIODIC or MIRROR) + */ +MISCMATHS::SpMat<float> Sparse3DBendingEnergyHessian(const std::vector<unsigned int>& isz, + const std::vector<double>& vxs, + MISCMATHS::BoundaryCondition bc) +{ + unsigned int mn = isz[0]*isz[1]*isz[2]; + unsigned int *irp = new unsigned int[3*mn]; // Worst case, might be slightly smaller + unsigned int *jcp = new unsigned int[mn+1]; + double *sp = new double[3*mn]; + // x-direction + unsigned int irp_cntr = 0; + double sf = 1 / vxs[0]; // Let us scale all directions to mm^{-1} + for (unsigned int k=0; k<isz[2]; k++) { + for (unsigned int j=0; j<isz[1]; j++) { + for (unsigned int i=0; i<isz[0]; i++) { + jcp[k*isz[0]*isz[1] + j*isz[0] + i] = irp_cntr; + if (bc == MISCMATHS::PERIODIC) { + if (i==0) { + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0]; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + 1; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + isz[0]-1; + sp[irp_cntr++] = -1.0 * sf; + } + else if (i==isz[0]-1) { + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0]; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i - 1; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + } + else { + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i - 1; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i + 1; + sp[irp_cntr++] = -1.0 * sf; + } + } + else if (bc == MISCMATHS::MIRROR) { + if (i==0) { + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0]; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + 1; + sp[irp_cntr++] = -2.0 * sf; + } + else if (i==isz[0]-1) { + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i - 1; + sp[irp_cntr++] = -2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + } + else { + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i - 1; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i + 1; + sp[irp_cntr++] = -1.0 * sf; + } + } + } + } + } + jcp[mn] = irp_cntr; + MISCMATHS::SpMat<float> At(mn,mn,irp,jcp,sp); + MISCMATHS::SpMat<float> AtA = At * At.t(); + + // y-direction + irp_cntr = 0; + sf = 1 / vxs[1]; // Let us scale all directions to mm^{-1} + for (unsigned int k=0; k<isz[2]; k++) { + for (unsigned int j=0; j<isz[1]; j++) { + for (unsigned int i=0; i<isz[0]; i++) { + jcp[k*isz[0]*isz[1] + j*isz[0] + i] = irp_cntr; + if (bc == MISCMATHS::PERIODIC) { + if (j==0) { + irp[irp_cntr] = k*isz[0]*isz[1] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + isz[0] + i; + sp[irp_cntr++] = -1.0; + irp[irp_cntr] = k*isz[0]*isz[1] + (isz[1]-1)*isz[0] + i; + sp[irp_cntr++] = -1.0; + } + else if (j==isz[1]-1) { + irp[irp_cntr] = k*isz[0]*isz[1] + i; + sp[irp_cntr++] = -1.0; + irp[irp_cntr] = k*isz[0]*isz[1] + (j-1)*isz[0] + i; + sp[irp_cntr++] = -1.0; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0; + } + else { + irp[irp_cntr] = k*isz[0]*isz[1] + (j-1)*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + (j+1)*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + } + } + else if (bc == MISCMATHS::MIRROR) { + if (j==0) { + irp[irp_cntr] = k*isz[0]*isz[1] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + isz[0] + i; + sp[irp_cntr++] = -2.0; + } + else if (j==isz[1]-1) { + irp[irp_cntr] = k*isz[0]*isz[1] + (j-1)*isz[0] + i; + sp[irp_cntr++] = -2.0; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0; + } + else { + irp[irp_cntr] = k*isz[0]*isz[1] + (j-1)*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + (j+1)*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + } + } + } + } + } + jcp[mn] = irp_cntr; + At = MISCMATHS::SpMat<float>(mn,mn,irp,jcp,sp); + AtA += At * At.t(); + + // z-direction + irp_cntr = 0; + sf = 1 / vxs[2]; // Let us scale all directions to mm^{-1} + for (unsigned int k=0; k<isz[2]; k++) { + for (unsigned int j=0; j<isz[1]; j++) { + for (unsigned int i=0; i<isz[0]; i++) { + jcp[k*isz[0]*isz[1] + j*isz[0] + i] = irp_cntr; + if (bc == MISCMATHS::PERIODIC) { + if (k==0) { + irp[irp_cntr] = j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = (isz[2]-1)*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + } + else if (k==isz[2]-1) { + irp[irp_cntr] = j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = (k-1)*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + } + else { + irp[irp_cntr] = (k-1)*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = (k+1)*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + } + } + else if (bc == MISCMATHS::MIRROR) { + if (k==0) { + irp[irp_cntr] = j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -2.0 * sf; + } + else if (k==isz[2]-1) { + irp[irp_cntr] = (k-1)*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -2.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + } + else { + irp[irp_cntr] = (k-1)*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + irp[irp_cntr] = k*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = 2.0 * sf; + irp[irp_cntr] = (k+1)*isz[0]*isz[1] + j*isz[0] + i; + sp[irp_cntr++] = -1.0 * sf; + } + } + } + } + } + jcp[mn] = irp_cntr; + At = MISCMATHS::SpMat<float>(mn,mn,irp,jcp,sp); + AtA += At * At.t(); + delete [] irp; delete [] jcp; delete [] sp; + return(AtA); +} diff --git a/SpMatMatrices.h b/SpMatMatrices.h new file mode 100644 index 0000000..66ee514 --- /dev/null +++ b/SpMatMatrices.h @@ -0,0 +1,36 @@ +// +// Declarations for functions generating specialised +// sparse matrices of type SpMat +// +// SpMatMatrices.h +// +// Declares global functions for generating specialised +// sparse matrices of type SpMat +// +// Jesper Andersson, FMRIB Image Analysis Group +// +// Copyright (C) 2019 University of Oxford +// +/* CCOPYRIGHT */ + +#ifndef SpMatMatrices_h +#define SpMatMatrices_h + +#include <vector> +#include "newmat.h" +#include "SpMat.h" + +namespace MISCMATHS { + +enum BoundaryCondition { MIRROR, PERIODIC }; + +/// Generates Symmetric Toeplitz matrix with first column given by col +SpMat<float> SparseSymmetricToeplitz(const NEWMAT::ColumnVector& col); + +/// Generates Hessian for Bending Energy on a regular 3D grid +SpMat<float> Sparse3DBendingEnergyHessian(const std::vector<unsigned int>& isz, // Matrix size + const std::vector<double>& vxs, // Voxel size + MISCMATHS::BoundaryCondition bc); // PERIODIC or MIRROR +} // End namespace MISCMATHS + +#endif // End #ifndef SpMatMatrices_h -- GitLab