Bug Summary

File:src/linalg/GMatrixSparse.cpp
Location:line 3499, column 46
Description:Array access (from variable 'Ci') results in a null pointer dereference

Annotated Source Code

1/***************************************************************************
2 * GMatrixSparse.cpp - Sparse matrix class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2006-2013 by Juergen Knoedlseder *
5 * ----------------------------------------------------------------------- *
6 * *
7 * This program is free software: you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation, either version 3 of the License, or *
10 * (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
19 * *
20 ***************************************************************************/
21/**
22 * @file GMatrixSparse.cpp
23 * @brief Sparse matrix class implementation
24 * @author Juergen Knoedlseder
25 */
26
27/* __ Includes ___________________________________________________________ */
28#ifdef HAVE_CONFIG_H1
29#include <config.h>
30#endif
31#include <cmath>
32#include "GException.hpp"
33#include "GTools.hpp"
34#include "GVector.hpp"
35#include "GMatrix.hpp"
36#include "GMatrixSparse.hpp"
37#include "GMatrixSymmetric.hpp"
38#include "GSparseSymbolic.hpp"
39#include "GSparseNumeric.hpp"
40
41/* __ Method name definitions ____________________________________________ */
42#define G_CONSTRUCTOR"GMatrixSparse::GMatrixSparse(int&, int&, int&)" "GMatrixSparse::GMatrixSparse(int&, int&, int&)"
43#define G_OP_MUL_VEC"GMatrixSparse::operator*(GVector&)" "GMatrixSparse::operator*(GVector&)"
44#define G_OP_ADD"GMatrixSparse::operator+=(GMatrixSparse&)" "GMatrixSparse::operator+=(GMatrixSparse&)"
45#define G_OP_SUB"GMatrixSparse::operator-=(GMatrixSparse&)" "GMatrixSparse::operator-=(GMatrixSparse&)"
46#define G_OP_MAT_MUL"GMatrixSparse::operator*=(GMatrixSparse&)" "GMatrixSparse::operator*=(GMatrixSparse&)"
47#define G_AT"GMatrixSparse::at(int&, int&)" "GMatrixSparse::at(int&, int&)"
48#define G_EXTRACT_ROW"GMatrixSymmetric::row(int&)" "GMatrixSymmetric::row(int&)"
49#define G_SET_ROW"GMatrixSymmetric::row(int&, GVector&)" "GMatrixSymmetric::row(int&, GVector&)"
50#define G_EXTRACT_COLUMN"GMatrixSymmetric::column(int&)" "GMatrixSymmetric::column(int&)"
51#define G_SET_COLUMN"GMatrixSymmetric::column(int&, GVector&)" "GMatrixSymmetric::column(int&, GVector&)"
52#define G_SET_COLUMN2"GMatrixSymmetric::column(int&, double*, int*, int)" "GMatrixSymmetric::column(int&, double*, int*, int)"
53#define G_ADD_TO_ROW"GMatrixSymmetric::add_to_row(int&, GVector&)" "GMatrixSymmetric::add_to_row(int&, GVector&)"
54#define G_ADD_TO_COLUMN"GMatrixSymmetric::add_to_column(int&, GVector&)" "GMatrixSymmetric::add_to_column(int&, GVector&)"
55#define G_ADD_TO_COLUMN2"GMatrixSymmetric::add_to_column(int&, double*," " int*, int)" "GMatrixSymmetric::add_to_column(int&, double*,"\
56 " int*, int)"
57#define G_CHOL_DECOMP"GMatrixSparse::cholesky_decompose(bool)" "GMatrixSparse::cholesky_decompose(bool)"
58#define G_CHOL_SOLVE"GMatrixSparse::cholesky_solver(GVector&, bool)" "GMatrixSparse::cholesky_solver(GVector&, bool)"
59#define G_STACK_INIT"GMatrixSparse::stack_init(int&, int&)" "GMatrixSparse::stack_init(int&, int&)"
60#define G_STACK_PUSH"GMatrixSparse::stack_push_column(double*, int*, int&," " int&)" "GMatrixSparse::stack_push_column(double*, int*, int&,"\
61 " int&)"
62#define G_STACK_FLUSH"GMatrixSparse::stack_flush(void)" "GMatrixSparse::stack_flush(void)"
63#define G_COPY_MEMBERS"GMatrixSparse::copy_members(GMatrixSparse&)" "GMatrixSparse::copy_members(GMatrixSparse&)"
64#define G_ALLOC_MEMBERS"GMatrixSparse::alloc_members(int&, int&, int&)" "GMatrixSparse::alloc_members(int&, int&, int&)"
65#define G_ALLOC"GMatrixSparse::alloc_elements(int&, int&)" "GMatrixSparse::alloc_elements(int&, int&)"
66#define G_FREE"GMatrixSparse::free_elements(int&, int&)" "GMatrixSparse::free_elements(int&, int&)"
67#define G_REMOVE_ZERO"GMatrixSparse::remove_zero_row_col(void)" "GMatrixSparse::remove_zero_row_col(void)"
68#define G_SYMPERM"cs_symperm(GMatrixSparse*, int*, int&)" "cs_symperm(GMatrixSparse*, int*, int&)"
69#define G_TRANSPOSE"cs_transpose(GMatrixSparse*, int)" "cs_transpose(GMatrixSparse*, int)"
70
71/* __ Macros _____________________________________________________________ */
72#define G_MIN(a,b)(((a) < (b)) ? (a) : (b)) (((a) < (b)) ? (a) : (b))
73#define G_MAX(a,b)(((a) > (b)) ? (a) : (b)) (((a) > (b)) ? (a) : (b))
74
75/* __ Coding definitions _________________________________________________ */
76
77/* __ Debug definitions __________________________________________________ */
78//#define G_DEBUG_SPARSE_PENDING // Analyse pending values
79//#define G_DEBUG_SPARSE_INSERTION // Analyse value insertion
80//#define G_DEBUG_SPARSE_ADDITION // Analyse value addition
81//#define G_DEBUG_SPARSE_COMPRESSION // Analyse zero row/column compression
82//#define G_DEBUG_SPARSE_MALLOC // Analyse memory management
83//#define G_DEBUG_SPARSE_STACK_PUSH // Analyse stack column pushing
84//#define G_DEBUG_SPARSE_STACK_FLUSH // Analyse stack column flushing
85
86
87/*==========================================================================
88 = =
89 = Constructors/destructors =
90 = =
91 ==========================================================================*/
92
93/***********************************************************************//**
94 * @brief Void matrix constructor
95 *
96 * Constructs empty sparse matrix. The number of rows and columns of the
97 * matrix will be zero.
98 *
99 * @todo Verify that the class is save against empty matrix objects.
100 ***************************************************************************/
101GMatrixSparse::GMatrixSparse(void) : GMatrixBase()
102{
103 // Initialise class members
104 init_members();
105
106 // Return
107 return;
108}
109
110
111
112/***********************************************************************//**
113 * @brief Matrix constructor
114 *
115 * @param[in] rows Number of rows [>0].
116 * @param[in] columns Number of columns [>0].
117 * @param[in] elements Number of allocated elements (defaults to 0).
118 *
119 * @exception GException::empty
120 * Specified number of rows or columns is not valid.
121 *
122 * Constructs sparse matrix of dimension @p rows times @p columns. The
123 * optional @p elements argument specifies the size of the physical
124 * memory that should be allocated. By default, no memory will be allocated
125 * and memory allocation will be performed on-the-fly. If the amount of
126 * required memory is larger than the size specified by @p elements,
127 * additional momeory will be allocated automatically on-the-fly.
128 *
129 * @todo Is there a real need to throw an empty exception? The class should
130 * be able to operate on empty matrices.
131 ***************************************************************************/
132GMatrixSparse::GMatrixSparse(const int& rows,
133 const int& columns,
134 const int& elements) :
135 GMatrixBase()
136{
137 // Continue only if matrix is valid
138 if (rows > 0 && columns > 0) {
2
Assuming 'rows' is > 0
3
Taking true branch
139
140 // Initialise private members for clean destruction
141 init_members();
4
Calling 'GMatrixSparse::init_members'
6
Returning from 'GMatrixSparse::init_members'
142
143 // Allocate matrix memory
144 alloc_members(rows, columns, elements);
145
146 }
147 else {
148 throw GException::empty(G_CONSTRUCTOR"GMatrixSparse::GMatrixSparse(int&, int&, int&)");
149 }
150
151 // Return
152 return;
153}
154
155
156/***********************************************************************//**
157 * @brief GMatrix to GMatrixSparse storage class convertor
158 *
159 * @param[in] matrix Generic matrix (GMatrix).
160 *
161 * Constructs a sparse matrix by using the number of rows and columns of
162 * a generic matrix and by assigning the elements of the generic matrix
163 * to the sparse matrix.
164 ***************************************************************************/
165GMatrixSparse::GMatrixSparse(const GMatrix& matrix) : GMatrixBase()
166{
167 // Initialise class members for clean destruction
168 init_members();
169
170 // Construct matrix
171 alloc_members(matrix.rows(), matrix.columns());
172
173 // Fill matrix column by column
174 for (int col = 0; col < m_cols; ++col) {
175 GVector vector = matrix.column(col);
176 this->column(col, vector);
177 }
178
179 // Return
180 return;
181}
182
183
184/***********************************************************************//**
185 * @brief GMatrixSymmetric to GMatrixSparse storage class convertor
186 *
187 * @param[in] matrix Symmetric matrix (GMatrixSymmetric).
188 *
189 * Constructs a sparse matrix by using the number of rows and columns of
190 * a symmetric matrix and by assigning the elements of the symmetric matrix
191 * to the sparse matrix.
192 ***************************************************************************/
193GMatrixSparse::GMatrixSparse(const GMatrixSymmetric& matrix) : GMatrixBase()
194{
195 // Initialise class members for clean destruction
196 init_members();
197
198 // Allocate matrix memory
199 alloc_members(matrix.rows(), matrix.columns());
200
201 // Fill matrix column by column
202 for (int col = 0; col < m_cols; ++col) {
203 GVector vector = matrix.column(col);
204 this->column(col, vector);
205 }
206
207 // Return
208 return;
209}
210
211
212/***********************************************************************//**
213 * @brief Copy constructor
214 *
215 * @param[in] matrix Matrix.
216 *
217 * Constructs matrix by copying an existing matrix.
218 ***************************************************************************/
219GMatrixSparse::GMatrixSparse(const GMatrixSparse& matrix) : GMatrixBase(matrix)
220{
221 // Initialise private members for clean destruction
222 init_members();
223
224 // Copy members. Note that the copy operation does not fill the
225 // pending element.
226 copy_members(matrix);
227
228 // Return
229 return;
230}
231
232
233/***********************************************************************//**
234 * @brief Destructor
235 ***************************************************************************/
236GMatrixSparse::~GMatrixSparse(void)
237{
238 // Free members
239 free_members();
240
241 // Return
242 return;
243}
244
245
246/*==========================================================================
247 = =
248 = Operators =
249 = =
250 ==========================================================================*/
251
252/***********************************************************************//**
253 * @brief Matrix assignment operator
254 *
255 * @param[in] matrix Matrix.
256 * @return Matrix.
257 *
258 * Assigns the content of another matrix to the actual matrix instance.
259 ***************************************************************************/
260GMatrixSparse& GMatrixSparse::operator=(const GMatrixSparse& matrix)
261{
262 // Execute only if object is not identical
263 if (this != &matrix) {
264
265 // Assign base class members. Note that this method will also perform
266 // the allocation of the matrix memory and the copy of the matrix
267 // attributes.
268 this->GMatrixBase::operator=(matrix);
269
270 // Free derived class members
271 free_members();
272
273 // Initialise derived class members
274 init_members();
275
276 // Copy derived class members
277 copy_members(matrix);
278
279 } // endif: object was not identical
280
281 // Return
282 return *this;
283}
284
285
286/***********************************************************************//**
287 * @brief Value assignment operator
288 *
289 * @param[in] value Value.
290 * @return Matrix.
291 *
292 * Assigns the specified @p value to all elements of the matrix.
293 ***************************************************************************/
294GMatrixSparse& GMatrixSparse::operator=(const double& value)
295{
296 // Fill any pending element to have a non-pending state
297 fill_pending();
298
299 // If value is 0 then simply reinitialize column start indices
300 if (value == 0) {
301
302 // Initialise column start indices to 0
303 for (int col = 0; col <= m_cols; ++col) {
304 m_colstart[col] = 0;
305 }
306
307 }
308
309 // ... otherwise fill column-wise
310 else {
311
312 // Set column vector
313 GVector column(m_rows);
314 column = value;
315
316 // Column-wise setting
317 for (int col = 0; col < m_cols; ++col) {
318 this->column(col, column);
319 }
320
321 }
322
323 // Return this object
324 return *this;
325}
326
327
328/***********************************************************************//**
329 * @brief Return reference to matrix element
330 *
331 * @param[in] row Matrix row [0,...,rows()-1].
332 * @param[in] column Matrix column [0,...,columns()-1].
333 * @return Reference to matrix element.
334 *
335 * Returns the reference to the matrix element at @p row and @p column. If
336 * the matrix element does not yet exist, a reference to the pending element
337 * with a value of 0.0 is returned.
338 ***************************************************************************/
339double& GMatrixSparse::operator()(const int& row, const int& column)
340{
341 // Fill pending element. This will set the value of the pending element
342 // to 0.0.
343 fill_pending();
344
345 // Get element
346 int inx = get_index(row, column);
347 double* value;
348 if (inx < 0) {
349 value = &m_fill_val;
350 m_fill_row = row;
351 m_fill_col = column;
352 }
353 else {
354 value = &(m_data[inx]);
355 }
356
357 // Return element
358 return *value;
359}
360
361
362/***********************************************************************//**
363 * @brief Return reference to matrix element (const version)
364 *
365 * @param[in] row Matrix row [0,...,rows()-1].
366 * @param[in] column Matrix column [0,...,columns()-1].
367 * @return Const reference to matrix element.
368 *
369 * Returns a const reference to the matrix element at @p row and @p column.
370 * If the matrix element does not yet exist, a reference to the zero element
371 * is returned. If the matrix element corresponds to the pending element,
372 * a reference to the pending element is returned. Otherwise, a reference
373 * to the matrix elements is returned.
374 ***************************************************************************/
375const double& GMatrixSparse::operator()(const int& row,
376 const int& column) const
377{
378 // Get element. We need here the zero element to return also a pointer
379 // for 0.0 entry that is not stored. Since we have the const version we
380 // don't have to care about modification of this zero value.
381 int inx = get_index(row, column);
382 double* value;
383 if (inx < 0) {
384 value = (double*)&m_zero;
385 }
386 else if (inx == m_elements) {
387 value = (double*)&m_fill_val;
388 }
389 else {
390 value = (double*)&(m_data[inx]);
391 }
392
393 // Return element
394 return *value;
395}
396
397
398/***********************************************************************//**
399 * @brief Vector multiplication
400 *
401 * @param[in] vector Vector.
402 *
403 * @exception GException::matrix_vector_mismatch
404 * Vector length differs from number of columns in matrix.
405 *
406 * This method performs a vector multiplication of a matrix. The vector
407 * multiplication will produce a vector. The matrix multiplication can only
408 * be performed when the number of matrix columns is equal to the length of
409 * the vector.
410 *
411 * The method includes any pending fill.
412 ***************************************************************************/
413GVector GMatrixSparse::operator*(const GVector& vector) const
414{
415 // Raise an exception if the matrix and vector dimensions are not compatible
416 if (m_cols != vector.size()) {
417 throw GException::matrix_vector_mismatch(G_OP_MUL_VEC"GMatrixSparse::operator*(GVector&)", vector.size(),
418 m_rows, m_cols);
419 }
420
421 // Initialise result vector
422 GVector result(m_rows);
423
424 // Multiply only if there are elements in matrix
425 if (m_elements > 0) {
426
427 // Perform vector multiplication
428 for (int col = 0; col < m_cols; ++col) {
429 int i_start = m_colstart[col];
430 int i_stop = m_colstart[col+1];
431 double* ptr_data = m_data + i_start;
432 int* ptr_rowinx = m_rowinx + i_start;
433 for (int i = i_start; i < i_stop; ++i) {
434 result[*ptr_rowinx++] += *ptr_data++ * vector[col];
435 }
436 }
437
438 // If fill is pending then add-in also this element into the product
439 if (m_fill_val != 0.0) {
440 result[m_fill_row] += m_fill_val * vector[m_fill_col];
441 #if defined(G_DEBUG_SPARSE_PENDING)
442 std::cout << G_OP_MUL_VEC"GMatrixSparse::operator*(GVector&)" << ": pending value " << m_fill_val
443 << " for location (" << m_fill_row << "," << m_fill_col
444 << ") has been used." << std::endl;
445 #endif
446 }
447
448 } // endif: there were elements in matrix
449
450 // Return result
451 return result;
452}
453
454
455/***********************************************************************//**
456 * @brief Negate matrix elements
457 *
458 * @return Matrix with negated elements.
459 ***************************************************************************/
460GMatrixSparse GMatrixSparse::operator-(void) const
461{
462 // Copy matrix
463 GMatrixSparse matrix = *this;
464
465 // Fill pending element
466 matrix.fill_pending();
467
468 // Negate all matrix elements
469 double* ptr = matrix.m_data;
470 for (int i = 0; i < matrix.m_elements; ++i, ++ptr) {
471 *ptr = -(*ptr);
472 }
473
474 // Return matrix
475 return matrix;
476}
477
478
479/***********************************************************************//**
480 * @brief Equalty operator
481 *
482 * @param[in] matrix Matrix.
483 *
484 * This operator checks if two matrices are identical. Two matrices are
485 * considered identical if they have the same dimensions and identicial
486 * elements.
487 *
488 * @todo Implement native sparse code
489 ***************************************************************************/
490bool GMatrixSparse::operator==(const GMatrixSparse &matrix) const
491{
492 // Initalise the result to 'equal matrices'
493 bool result = true;
494
495 // Perform comparison (only if matrix dimensions are identical)
496 if (m_rows == matrix.m_rows && m_cols == matrix.m_cols) {
497
498 // Loop over all matrix elements
499 for (int row = 0; row < m_rows; ++row) {
500 for (int col = 0; col < m_cols; ++col) {
501 if ((*this)(row,col) != matrix(row,col)) {
502 result = false;
503 break;
504 }
505 }
506 if (!result) {
507 break;
508 }
509 }
510 }
511 else {
512 result = false;
513 }
514
515 // Return result
516 return result;
517}
518
519
520/***********************************************************************//**
521 * @brief Non-equality operator
522 *
523 * @param[in] matrix Matrix.
524 *
525 * This operator checks if two matrices are not identical. Two matrices are
526 * considered not identical if they differ in their dimensions or if at
527 * least one element differs.
528 ***************************************************************************/
529bool GMatrixSparse::operator!=(const GMatrixSparse &matrix) const
530{
531 // Get negated result of equality operation
532 bool result = !(this->operator==(matrix));
533
534 // Return result
535 return result;
536}
537
538
539/***********************************************************************//**
540 * @brief Unary matrix addition operator
541 *
542 * @param[in] matrix Matrix.
543 *
544 * @exception GException::matrix_mismatch
545 * Incompatible matrix size.
546 *
547 * This method performs a matrix addition. The operation can only succeed
548 * when the dimensions of both matrices are identical.
549 *
550 * @todo Implement native sparse code
551 ***************************************************************************/
552GMatrixSparse& GMatrixSparse::operator+=(const GMatrixSparse& matrix)
553{
554 // Raise an exception if the matrix dimensions are not compatible
555 if (m_rows != matrix.m_rows || m_cols != matrix.m_cols) {
556 throw GException::matrix_mismatch(G_OP_ADD"GMatrixSparse::operator+=(GMatrixSparse&)",
557 m_rows, m_cols,
558 matrix.m_rows, matrix.m_cols);
559 }
560
561 // Perform inplace matrix addition using vectors
562 for (int col = 0; col < m_cols; ++col) {
563 GVector v_result = column(col);
564 GVector v_operand = matrix.column(col);
565 v_result += v_operand;
566 column(col, v_result);
567 }
568
569 // Return result
570 return *this;
571}
572
573
574/***********************************************************************//**
575 * @brief Unary matrix subtraction operator
576 *
577 * @param[in] matrix Matrix.
578 *
579 * @exception GException::matrix_mismatch
580 * Incompatible matrix size.
581 *
582 * This method performs a matrix addition. The operation can only succeed
583 * when the dimensions of both matrices are identical.
584 *
585 * @todo Implement native sparse code
586 ***************************************************************************/
587GMatrixSparse& GMatrixSparse::operator-=(const GMatrixSparse& matrix)
588{
589 // Raise an exception if the matrix dimensions are not compatible
590 if (m_rows != matrix.m_rows || m_cols != matrix.m_cols) {
591 throw GException::matrix_mismatch(G_OP_SUB"GMatrixSparse::operator-=(GMatrixSparse&)",
592 m_rows, m_cols,
593 matrix.m_rows, matrix.m_cols);
594 }
595
596 // Perform inplace matrix subtraction
597 for (int col = 0; col < m_cols; ++col) {
598 GVector v_result = column(col);
599 GVector v_operand = matrix.column(col);
600 v_result -= v_operand;
601 column(col, v_result);
602 }
603
604 // Return result
605 return *this;
606}
607
608
609/***********************************************************************//**
610 * @brief Unary matrix multiplication operator
611 *
612 * @param[in] matrix Matrix.
613 *
614 * @exception GException::matrix_mismatch
615 * Incompatible matrix size.
616 *
617 * This method performs a matrix multiplication. The operation can only
618 * succeed when the dimensions of both matrices are compatible.
619 *
620 * @todo Implement native sparse code
621 ***************************************************************************/
622GMatrixSparse& GMatrixSparse::operator*=(const GMatrixSparse& matrix)
623{
624 // Raise an exception if the matrix dimensions are not compatible
625 if (m_cols != matrix.m_rows) {
626 throw GException::matrix_mismatch(G_OP_MAT_MUL"GMatrixSparse::operator*=(GMatrixSparse&)",
627 m_rows, m_cols,
628 matrix.m_rows, matrix.m_cols);
629 }
630
631 // Allocate result matrix
632 GMatrixSparse result(m_rows, matrix.m_cols);
633
634 // Multiply only if there are elements in both matrices
635 if (m_elements > 0 && matrix.m_elements > 0) {
636
637 // Loop over all elements of result matrix
638 for (int row = 0; row < m_rows; ++row) {
639 for (int col = 0; col < matrix.m_cols; ++col) {
640 double sum = 0.0;
641 for (int i = 0; i < m_cols; ++i) {
642 sum += (*this)(row,i) * matrix(i,col);
643 }
644 result(row,col) = sum;
645 }
646 }
647
648 }
649
650 // Assign result
651 *this = result;
652
653 // Return result
654 return *this;
655}
656
657
658/*==========================================================================
659 = =
660 = Public methods =
661 = =
662 ==========================================================================*/
663
664/***********************************************************************//**
665 * @brief Clear matrix
666 ***************************************************************************/
667void GMatrixSparse::clear(void)
668{
669 // Free members
670 free_members();
671
672 // Initialise private members
673 init_members();
674
675 // Return
676 return;
677}
678
679
680/***********************************************************************//**
681 * @brief Clone matrix
682 *
683 * @return Pointer to deep copy of matrix.
684 ***************************************************************************/
685GMatrixSparse* GMatrixSparse::clone(void) const
686{
687 // Clone matrix
688 return new GMatrixSparse(*this);
689}
690
691
692/***********************************************************************//**
693 * @brief Return reference to matrix element
694 *
695 * @param[in] row Matrix row [0,...,rows()-1].
696 * @param[in] column Matrix column [0,...,columns()-1].
697 * @return Reference to matrix element.
698 *
699 * @exception GException::out_of_range
700 * Row or column index out of range.
701 ***************************************************************************/
702double& GMatrixSparse::at(const int& row, const int& column)
703{
704 // Raise exception if row or column index is out of range
705 if (row < 0 || row >= m_rows || column < 0 || column >= m_cols) {
706 throw GException::out_of_range(G_AT"GMatrixSparse::at(int&, int&)", row, column, m_rows, m_cols);
707 }
708
709 // Return element
710 return ((*this)(row, column));
711}
712
713
714/***********************************************************************//**
715 * @brief Return reference to matrix element (const version)
716 *
717 * @param[in] row Matrix row [0,...,rows()-1].
718 * @param[in] column Matrix column [0,...,columns()-1].
719 * @return Reference to matrix element.
720 *
721 * @exception GException::out_of_range
722 * Row or column index out of range.
723 ***************************************************************************/
724const double& GMatrixSparse::at(const int& row, const int& column) const
725{
726 // Raise exception if row or column index is out of range
727 if (row < 0 || row >= m_rows || column < 0 || column >= m_cols) {
728 throw GException::out_of_range(G_AT"GMatrixSparse::at(int&, int&)", row, column, m_rows, m_cols);
729 }
730
731 // Return element
732 return ((*this)(row, column));
733}
734
735
736/***********************************************************************//**
737 * @brief Extract row as vector from matrix
738 *
739 * @param[in] row Row to be extracted (starting from 0).
740 *
741 * @exception GException::out_of_range
742 * Invalid row index specified.
743 *
744 * This method extracts a matrix row into a vector.
745 ***************************************************************************/
746GVector GMatrixSparse::row(const int& row) const
747{
748 // Raise an exception if the row index is invalid
749 #if defined(G_RANGE_CHECK1)
750 if (row < 0 || row >= m_rows) {
751 throw GException::out_of_range(G_EXTRACT_ROW"GMatrixSymmetric::row(int&)", row, 0, m_rows-1);
752 }
753 #endif
754
755 // Create result vector
756 GVector result(m_cols);
757
758 // Loop over all columns to extract data
759 for (int col = 0; col < m_cols; ++col) {
760
761 // Get the start and stop of the elements
762 int i_start = m_colstart[col];
763 int i_stop = m_colstart[col+1];
764
765 // Search requested row in elements
766 int i;
767 for (i = i_start; i < i_stop; ++i) {
768 if (m_rowinx[i] == row) {
769 break;
770 }
771 }
772
773 // Copy element if we found one
774 if (i < i_stop) {
775 result[col] = m_data[i];
776 }
777
778 } // endfor: looped over all columns
779
780 // If there is a pending element then put it in the vector
781 if (m_fill_val != 0.0 && m_fill_row == row) {
782 result[m_fill_col] = m_fill_val;
783 }
784
785 // Return vector
786 return result;
787}
788
789
790/***********************************************************************//**
791 * @brief Set row in matrix
792 *
793 * @todo To be implemented.
794 ***************************************************************************/
795void GMatrixSparse::row(const int& row, const GVector& vector)
796{
797 // Raise an exception if the row index is invalid
798 #if defined(G_RANGE_CHECK1)
799 if (row < 0 || row >= m_rows) {
800 throw GException::out_of_range(G_SET_ROW"GMatrixSymmetric::row(int&, GVector&)", row, 0, m_rows-1);
801 }
802 #endif
803
804 // Return
805 return;
806}
807
808
809/***********************************************************************//**
810 * @brief Extract column as vector from matrix
811 *
812 * @param[in] column Column index [0,...,columns()-1].
813 *
814 * @exception GException::out_of_range
815 * Invalid row index specified.
816 *
817 * This method extracts a matrix column into a vector.
818 ***************************************************************************/
819GVector GMatrixSparse::column(const int& column) const
820{
821 // Raise an exception if the column index is invalid
822 #if defined(G_RANGE_CHECK1)
823 if (column < 0 || column >= m_cols) {
824 throw GException::out_of_range(G_EXTRACT_COLUMN"GMatrixSymmetric::column(int&)", column, 0, m_cols-1);
825 }
826 #endif
827
828 // Create result vector
829 GVector result(m_rows);
830
831 // Get the start and stop of the elements
832 int i_start = m_colstart[column];
833 int i_stop = m_colstart[column+1];
834
835 // Extract elements into vector
836 for (int i = i_start; i < i_stop; ++i) {
837 result[m_rowinx[i]] = m_data[i];
838 }
839
840 // If there is a pending element then put it in the vector
841 if (m_fill_val != 0.0 && m_fill_col == column) {
842 result[m_fill_row] = m_fill_val;
843 }
844
845 // Return vector
846 return result;
847}
848
849
850/***********************************************************************//**
851 * @brief Insert vector column into matrix
852 *
853 * @param[in] column Column index [0,...,columns()-1].
854 * @param[in] vector Vector.
855 *
856 * @exception GException::out_of_range
857 * Invalid column index specified.
858 * @exception GException::matrix_vector_mismatch
859 * Matrix dimension mismatches the vector size.
860 *
861 * Inserts the content of a vector into a matrix column. Any previous
862 * content in the matrix column will be overwritted.
863 *
864 * This is the main driver routine to insert data into a matrix. Note that
865 * there is another instance of this function that takes a compressed array.
866 ***************************************************************************/
867void GMatrixSparse::column(const int& column, const GVector& vector)
868{
869 // Debug header
870 #if defined(G_DEBUG_SPARSE_INSERTION)
871 std::cout << "GMatrixSparse::column(";
872 std::cout << column << ", [" << v << "]):" << std::endl;
873 std::cout << " In Data : ";
874 for (int i = 0; i < m_elements; ++i) {
875 std::cout << m_data[i] << " ";
876 }
877 std::cout << std::endl << " In Row .: ";
878 for (int i = 0; i < m_elements; ++i) {
879 std::cout << m_rowinx[i] << " ";
880 }
881 std::cout << std::endl << " In Col .: ";
882 for (int i = 0; i < m_cols+1; ++i) {
883 std::cout << m_colstart[i] << " ";
884 }
885 std::cout << std::endl;
886 #endif
887
888 // Raise an exception if the column index is invalid
889 #if defined(G_RANGE_CHECK1)
890 if (column < 0 || column >= m_cols) {
891 throw GException::out_of_range(G_SET_COLUMN"GMatrixSymmetric::column(int&, GVector&)", column, 0, m_cols-1);
892 }
893 #endif
894
895 // Raise an exception if the matrix and vector dimensions are not
896 // compatible
897 if (m_rows != vector.size()) {
898 throw GException::matrix_vector_mismatch(G_SET_COLUMN"GMatrixSymmetric::column(int&, GVector&)", vector.size(),
899 m_rows, m_cols);
900 }
901
902 // If there is a pending element for this column then delete it since
903 // the vector overwrites this element
904 if (m_fill_val != 0.0 && m_fill_col == column) {
905 #if defined(G_DEBUG_SPARSE_PENDING)
906 std::cout << G_SET_COLUMN"GMatrixSymmetric::column(int&, GVector&)" << ": pending value " << m_fill_val <<
907 " for location (" << m_fill_row << "," << m_fill_col <<
908 ") became obsolete" << std::endl;
909 #endif
910 m_fill_val = 0.0;
911 m_fill_row = 0;
912 m_fill_col = 0;
913 }
914
915 // Determine the number of non-zero elements in the vector
916 int n_vector = 0;
917 for (int row = 0; row < m_rows; ++row) {
918 if (vector[row] != 0.0) {
919 n_vector++;
920 }
921 }
922
923 // Get the start and stop indices of the actual column and compute
924 // the number of exisiting elements in the column
925 int i_start = m_colstart[column];
926 int i_stop = m_colstart[column+1];
927 int n_exist = i_stop - i_start;
928
929 // Compute the size difference for the new matrix. It is positive if
930 // the number of non-zero entries in the vector is larger than the
931 // number of non-zero entries in the matrix (in this case we have to
932 // increase the matrix size).
933 int n_diff = n_vector - n_exist;
934
935 // If we need space then allocate it, if we have to much space then free it
936 if (n_diff > 0) {
937 alloc_elements(i_start, n_diff);
938 #if defined(G_DEBUG_SPARSE_INSERTION)
939 std::cout << " Insert .: " << n_diff << " elements at index " << i_start << std::endl;
940 #endif
941 }
942 else if (n_diff < 0) {
943 free_elements(i_start, -n_diff);
944 #if defined(G_DEBUG_SPARSE_INSERTION)
945 std::cout << " Remove .: " << -n_diff << " elements at index " << i_start << std::endl;
946 #endif
947 }
948
949 // Insert the vector elements in the matrix
950 if (n_vector > 0) {
951 for (int row = 0, i = i_start; row < m_rows; ++row) {
952 if (vector[row] != 0.0) {
953 m_data[i] = vector[row];
954 m_rowinx[i] = row;
955 i++;
956 }
957 }
958 }
959
960 // Update column start indices
961 for (int i = column+1; i <= m_cols; ++i) {
962 m_colstart[i] += n_diff;
963 }
964
965 // Debugging: show sparse matrix after insertion
966 #if defined(G_DEBUG_SPARSE_INSERTION)
967 std::cout << " Out Data: ";
968 for (int i = 0; i < m_elements; ++i) {
969 std::cout << m_data[i] << " ";
970 }
971 std::cout << std::endl << " Out Row : ";
972 for (int i = 0; i < m_elements; ++i) {
973 std::cout << m_rowinx[i] << " ";
974 }
975 std::cout << endl << " Out Col : ";
976 for (int i = 0; i < m_cols+1; ++i) {
977 std::cout << m_colstart[i] << " ";
978 }
979 std::cout << std::endl;
980 #endif
981
982 // Return
983 return;
984}
985
986
987/***********************************************************************//**
988 * @brief Insert compressed array into matrix column
989 *
990 * @param[in] column Column index [0,...,columns()-1].
991 * @param[in] values Compressed array.
992 * @param[in] rows Row indices of array.
993 * @param[in] number Number of elements in array.
994 *
995 * @exception GException::out_of_range
996 * Invalid column index specified.
997 * @exception GException::matrix_vector_mismatch
998 * Matrix dimension mismatches the vector size.
999 *
1000 * Inserts the content of a copressed array into a matrix column. Any
1001 * previous content in the matrix column will be overwritted.
1002 *
1003 * This is the main driver routine to insert data into a matrix. Note that
1004 * there is another instance of this function that takes a vector.
1005 ***************************************************************************/
1006void GMatrixSparse::column(const int& column, const double* values,
1007 const int* rows, int number)
1008{
1009 // Debug header
1010 #if defined(G_DEBUG_SPARSE_INSERTION)
1011 std::cout << "GMatrixSparse::column(";
1012 std::cout << column << ", values, rows, " << number << "):" << std::endl;
1013 std::cout << " Matrix Data : ";
1014 for (int i = 0; i < m_elements; ++i) {
1015 std::cout << m_data[i] << " ";
1016 }
1017 std::cout << std::endl << " Matrix Row .: ";
1018 for (int i = 0; i < m_elements; ++i) {
1019 std::cout << m_rowinx[i] << " ";
1020 }
1021 std::cout << std::endl << " Matrix Col .: ";
1022 for (int i = 0; i < m_cols+1; ++i) {
1023 std::cout << m_colstart[i] << " ";
1024 }
1025 std::cout << std::endl;
1026 std::cout << " Array Data .: ";
1027 for (int i = 0; i < number; ++i) {
1028 std::cout << values[i] << " ";
1029 }
1030 std::cout << std::endl << " Array Row ..: ";
1031 for (int i = 0; i < number; ++i) {
1032 std::cout << rows[i] << " ";
1033 }
1034 std::cout << std::endl;
1035 #endif
1036
1037 // Raise an exception if the column index is invalid
1038 #if defined(G_RANGE_CHECK1)
1039 if (column < 0 || column >= m_cols) {
1040 throw GException::out_of_range(G_SET_COLUMN2"GMatrixSymmetric::column(int&, double*, int*, int)", column, 0, m_cols-1);
1041 }
1042 #endif
1043
1044 // Raise an exception if the index array seems incompatible with matrix
1045 // dimensions
1046 if (rows[number-1] >= m_rows) {
1047 throw GException::matrix_vector_mismatch(G_SET_COLUMN2"GMatrixSymmetric::column(int&, double*, int*, int)", rows[number-1],
1048 m_rows, m_cols);
1049 }
1050
1051 // If there is a pending element for this column then delete it since
1052 // the vector overwrites this element
1053 if (m_fill_val != 0.0 && m_fill_col == column) {
1054 #if defined(G_DEBUG_SPARSE_PENDING)
1055 std::cout << G_INSERT_COL2 << ": pending value " << m_fill_val <<
1056 " for location (" << m_fill_row << "," << m_fill_col <<
1057 ") became obsolete" << std::endl;
1058 #endif
1059 m_fill_val = 0.0;
1060 m_fill_row = 0;
1061 m_fill_col = 0;
1062 }
1063
1064 // Get the start and stop indices of the actual column and compute
1065 // the number of exisiting elements in the column
1066 int i_start = m_colstart[column];
1067 int i_stop = m_colstart[column+1];
1068 int n_exist = i_stop - i_start;
1069
1070 // If the array is empty then make sure that the number of elements is 0
1071 // (we then just delete the existing column)
1072 if (!values || !rows) {
1073 number = 0;
1074 }
1075
1076 // Compute the size difference for the new matrix. It is positive if
1077 // the number of non-zero entries in the array is larger than the
1078 // number of non-zero entries in the matrix (in this case we have to
1079 // increase the matrix size).
1080 int n_diff = number - n_exist;
1081
1082 // If we need space then allocate it, if we have to much space then free it
1083 if (n_diff > 0) {
1084 alloc_elements(i_start, n_diff);
1085 #if defined(G_DEBUG_SPARSE_INSERTION)
1086 std::cout << " Insert .: " << n_diff << " elements at index " << i_start << std::endl;
1087 #endif
1088 }
1089 else if (n_diff < 0) {
1090 free_elements(i_start, -n_diff);
1091 #if defined(G_DEBUG_SPARSE_INSERTION)
1092 std::cout << " Remove .: " << -n_diff << " elements at index " << i_start << std::endl;
1093 #endif
1094 }
1095
1096 // Insert the array elements into the matrix
1097 if (number > 0) {
1098 for (int row = 0, i = i_start; row < number; ++row, ++i) {
1099 m_data[i] = values[row];
1100 m_rowinx[i] = rows[row];
1101 }
1102 }
1103
1104 // Update column start indices
1105 for (int i = column+1; i <= m_cols; ++i) {
1106 m_colstart[i] += n_diff;
1107 }
1108
1109 // Debugging: show sparse matrix after insertion
1110 #if defined(G_DEBUG_SPARSE_INSERTION)
1111 std::cout << " Out Data: ";
1112 for (int i = 0; i < m_elements; ++i) {
1113 std::cout << m_data[i] << " ";
1114 }
1115 std::cout << std::endl << " Out Row : ";
1116 for (int i = 0; i < m_elements; ++i) {
1117 std::cout << m_rowinx[i] << " ";
1118 }
1119 std::cout << std::endl << " Out Col : ";
1120 for (int i = 0; i < m_cols+1; ++i) {
1121 std::cout << m_colstart[i] << " ";
1122 }
1123 std::cout << std::endl;
1124 #endif
1125
1126 // Return
1127 return;
1128}
1129
1130
1131/***********************************************************************//**
1132 * @brief Add row to matrix elements
1133 *
1134 * @todo To be implemented.
1135 ***************************************************************************/
1136void GMatrixSparse::add_to_row(const int& row, const GVector& vector)
1137{
1138 // Raise an exception if the row index is invalid
1139 #if defined(G_RANGE_CHECK1)
1140 if (row < 0 || row >= m_rows) {
1141 throw GException::out_of_range(G_ADD_TO_ROW"GMatrixSymmetric::add_to_row(int&, GVector&)", row, 0, m_rows-1);
1142 }
1143 #endif
1144
1145 // Return
1146 return;
1147}
1148
1149
1150/***********************************************************************//**
1151 * @brief Add vector column into matrix
1152 *
1153 * @param[in] column Column index [0,...,columns()-1].
1154 * @param[in] vector Vector.
1155 *
1156 * @exception GException::out_of_range
1157 * Invalid column index specified.
1158 * @exception GException::matrix_vector_mismatch
1159 * Matrix dimension mismatches the vector size.
1160 *
1161 * Adds the contect of a vector to a matrix column.
1162 *
1163 * This is the main driver routine to add data to a matrix. It handles both
1164 * normal and stack-based filled. Note that there is another instance of this
1165 * method that takes a compressed array.
1166 ***************************************************************************/
1167void GMatrixSparse::add_to_column(const int& column, const GVector& vector)
1168{
1169 // Debug header
1170 #if defined(G_DEBUG_SPARSE_ADDITION)
1171 std::cout << "GMatrixSparse::add_col(";
1172 std::cout << column << ", [" << v << "]):" << std::endl;
1173 std::cout << " In Data : ";
1174 for (int i = 0; i < m_elements; ++i) {
1175 std::cout << m_data[i] << " ";
1176 }
1177 std::cout << std::endl << " In Row .: ";
1178 for (int i = 0; i < m_elements; ++i) {
1179 std::cout << m_rowinx[i] << " ";
1180 }
1181 std::cout << std::endl << " In Col .: ";
1182 for (int i = 0; i < m_cols+1; ++i) {
1183 std::cout << m_colstart[i] << " ";
1184 }
1185 std::cout << std::endl;
1186 #endif
1187
1188 // Initialise number of non-zero elements to 0
1189 int non_zero = 0;
1190
1191 // If we have a stack then try to push vector on stack first. Note that
1192 // stack_push_column does its own argument verifications, so to avoid
1193 // double checking we don't do anything before this call ...
1194 if (m_stack_data != NULL__null) {
1195 non_zero = stack_push_column(vector, column);
1196 if (non_zero == 0) {
1197 return;
1198 }
1199 }
1200
1201 // ... otherwise check first the arguments and determine the number of
1202 // non-zero elements in vector
1203 else {
1204
1205 // Raise an exception if the column index is invalid
1206 #if defined(G_RANGE_CHECK1)
1207 if (column < 0 || column >= m_cols) {
1208 throw GException::out_of_range(G_ADD_TO_COLUMN"GMatrixSymmetric::add_to_column(int&, GVector&)", column, 0, m_cols-1);
1209 }
1210 #endif
1211
1212 // Raise an exception if the matrix and vector dimensions are incompatible
1213 if (m_rows != vector.size()) {
1214 throw GException::matrix_vector_mismatch(G_ADD_TO_COLUMN"GMatrixSymmetric::add_to_column(int&, GVector&)", vector.size(),
1215 m_rows, m_cols);
1216 }
1217
1218 // Determine number of non-zero elements in vector
1219 non_zero = vector.non_zeros();
1220 }
1221
1222 // Extract vector for column, add elements, and re-insert vector (only if
1223 // vector to insert has non-zeros)
1224 if (non_zero > 0) {
1225
1226 // Copy input vector
1227 GVector v_column = vector;
1228
1229 // Add elements to vector
1230 for (int i = m_colstart[column]; i < m_colstart[column+1]; ++i) {
1231 v_column[m_rowinx[i]] += m_data[i];
1232 }
1233
1234 // If there is a pending element then put it in the vector
1235 if (m_fill_val != 0.0 && m_fill_col == column) {
1236 v_column[m_fill_row] += m_fill_val;
1237 }
1238
1239 // Set vector into matrix
1240 this->column(column, v_column);
1241
1242 }
1243
1244 // Debugging: show sparse matrix after addition
1245 #if defined(G_DEBUG_SPARSE_ADDITION)
1246 std::cout << " Out Data: ";
1247 for (int i = 0; i < m_elements; ++i) {
1248 std::cout << m_data[i] << " ";
1249 }
1250 std::cout << std::endl << " Out Row : ";
1251 for (int i = 0; i < m_elements; ++i) {
1252 std::cout << m_rowinx[i] << " ";
1253 }
1254 std::cout << std::endl << " Out Col : ";
1255 for (int i = 0; i < m_cols+1; ++i) {
1256 std::cout << m_colstart[i] << " ";
1257 }
1258 std::cout << std::endl;
1259 #endif
1260
1261 // Return
1262 return;
1263}
1264
1265
1266/***********************************************************************//**
1267 * @brief Add compressed array into matrix column
1268 *
1269 * @param[in] column Column index [0,...,columns()-1].
1270 * @param[in] values Compressed array.
1271 * @param[in] rows Row indices of array.
1272 * @param[in] number Number of elements in array.
1273 *
1274 * @exception GException::out_of_range
1275 * Invalid column index specified.
1276 * @exception GException::matrix_vector_mismatch
1277 * Matrix dimension mismatches the vector size.
1278 *
1279 * Adds the content of a compressed array into a matrix column.
1280 *
1281 * This is the main driver routine to add data to a matrix. It handles both
1282 * normal and stack-based filled. Note that there is another instance of this
1283 * method that takes a vector.
1284 ***************************************************************************/
1285void GMatrixSparse::add_to_column(const int& column, const double* values,
1286 const int* rows, int number)
1287{
1288 // Debug header
1289 #if defined(G_DEBUG_SPARSE_ADDITION)
1290 std::cout << "GMatrixSparse::add_col(";
1291 std::cout << column << ", values, rows, " << number << "):" << std::endl;
1292 std::cout << " Matrix Data : ";
1293 for (int i = 0; i < m_elements; ++i) {
1294 std::cout << m_data[i] << " ";
1295 }
1296 std::cout << std::endl << " Matrix Row .: ";
1297 for (int i = 0; i < m_elements; ++i) {
1298 std::cout << m_rowinx[i] << " ";
1299 }
1300 std::cout << std::endl << " Matrix Col .: ";
1301 for (int i = 0; i < m_cols+1; ++i) {
1302 std::cout << m_colstart[i] << " ";
1303 }
1304 std::cout << std::endl;
1305 std::cout << " Array Data .: ";
1306 for (int i = 0; i < number; ++i) {
1307 std::cout << values[i] << " ";
1308 }
1309 std::cout << std::endl << " Array Row ..: ";
1310 for (int i = 0; i < number; ++i) {
1311 std::cout << rows[i] << " ";
1312 }
1313 std::cout << std::endl;
1314 #endif
1315
1316 // If we have a stack then try to push elements on stack first. Note that
1317 // stack_push_column does its own argument verifications, so to avoid
1318 // double checking we don't do anything before this call ...
1319 if (m_stack_data != NULL__null) {
1320 number = stack_push_column(values, rows, number, column);
1321 if (number == 0) {
1322 return;
1323 }
1324 }
1325
1326 // ... otherwise check the arguments
1327 else {
1328 // If the array is empty there is nothing to do
1329 if (!values || !rows || (number < 1)) {
1330 return;
1331 }
1332
1333 // Raise an exception if the column index is invalid
1334 #if defined(G_RANGE_CHECK1)
1335 if (column < 0 || column >= m_cols) {
1336 throw GException::out_of_range(G_ADD_TO_COLUMN2"GMatrixSymmetric::add_to_column(int&, double*," " int*, int)", column, 0, m_cols-1);
1337 }
1338 #endif
1339
1340 // Raise an exception if the matrix and vector dimensions are incompatible
1341 if (rows[number-1] >= m_rows) {
1342 throw GException::matrix_vector_mismatch(G_ADD_TO_COLUMN2"GMatrixSymmetric::add_to_column(int&, double*," " int*, int)", rows[number-1],
1343 m_rows, m_cols);
1344 }
1345
1346 } // endelse: there was no stack
1347
1348 // Get indices of column in matrix
1349 int i_start = m_colstart[column];
1350 int i_stop = m_colstart[column+1];
1351
1352 // Case A: the column exists in the matrix, so mix new elements with existing
1353 // data
1354 if (i_start < i_stop) {
1355
1356 // Fill pending element before the merge (it will be lost otherwise)
1357 fill_pending();
1358
1359 // Allocate workspace to hold combined column
1360 int wrk_size = number + i_stop - i_start;
1361 double* wrk_double = new double[wrk_size];
1362 int* wrk_int = new int[wrk_size];
1363
1364 // Mix matrix column with specified data
1365 int num_mix;
1366 mix_column(&(m_data[i_start]), &(m_rowinx[i_start]), i_stop-i_start,
1367 values, rows, number,
1368 wrk_double, wrk_int, &num_mix);
1369
1370 // Insert mixed column
1371 this->column(column, wrk_double, wrk_int, num_mix);
1372
1373 // Free workspace
1374 delete [] wrk_int;
1375 delete [] wrk_double;
1376
1377 } // endif: Case A
1378
1379 // Case B: the column does not yet exist in the matrix, so just insert it
1380 else {
1381 this->column(column, values, rows, number);
1382 }
1383
1384 // Debugging: show sparse matrix after insertion
1385 #if defined(G_DEBUG_SPARSE_ADDITION)
1386 std::cout << " Out Data: ";
1387 for (int i = 0; i < m_elements; ++i) {
1388 std::cout << m_data[i] << " ";
1389 }
1390 std::cout << std::endl << " Out Row : ";
1391 for (int i = 0; i < m_elements; ++i) {
1392 std::cout << m_rowinx[i] << " ";
1393 }
1394 std::cout << std::endl << " Out Col : ";
1395 for (int i = 0; i < m_cols+1; ++i) {
1396 std::cout << m_colstart[i] << " ";
1397 }
1398 std::cout << std::endl;
1399 #endif
1400
1401 // Return
1402 return;
1403}
1404
1405
1406/***********************************************************************//**
1407 * @brief Return transposed matrix
1408 *
1409 * @return Transposed matrix.
1410 *
1411 * Returns transposed matrix of the matrix.
1412 ***************************************************************************/
1413GMatrixSparse GMatrixSparse::transpose(void) const
1414{
1415 // Copy matrix
1416 GMatrixSparse matrix = *this;
1417
1418 // Fill pending element
1419 matrix.fill_pending();
1420
1421 // Compute the transpose
1422 matrix = cs_transpose(matrix, 1);
1423
1424 // Return matrix
1425 return matrix;
1426}
1427
1428
1429/***********************************************************************//**
1430 * @brief Return inverted matrix
1431 *
1432 * @return Inverted matrix.
1433 *
1434 * Returns inverse of matrix. Inversion is done for the moment using Cholesky
1435 * decomposition. This does not work on any kind of matrix.
1436 *
1437 * @todo Specify in documentation for which kind of matrix the method works.
1438 ***************************************************************************/
1439GMatrixSparse GMatrixSparse::invert(void) const
1440{
1441 // Allocate result matrix
1442 GMatrixSparse matrix(m_cols, m_rows);
1443
1444 // Invert matrix
1445 matrix.cholesky_invert(true);
1446
1447 // Return matrix
1448 return matrix;
1449}
1450
1451
1452/***********************************************************************//**
1453 * @brief Solves linear matrix equation
1454 *
1455 * @param[in] vector Solution vector.
1456 *
1457 * Solves the linear equation
1458 *
1459 * \f[M \times {\tt solution} = {\tt vector} \f]
1460 *
1461 * where \f$M\f$ is the matrix, \f${\tt vector}\f$ is the result, and
1462 * \f${\tt solution}\f$ is the solution. Solving is done using Cholesky
1463 * decomposition. This does not work on any kind of matrix.
1464 *
1465 * @todo Specify in documentation for which kind of matrix the method works.
1466 ***************************************************************************/
1467GVector GMatrixSparse::solve(const GVector& vector) const
1468{
1469 // Get Cholesky decomposition of matrix
1470 GMatrixSparse decomposition = cholesky_decompose(true);
1471
1472 // Solve linear equation
1473 GVector result = decomposition.cholesky_solver(vector);
1474
1475 // Return result
1476 return result;
1477}
1478
1479
1480/***********************************************************************//**
1481 * @brief Return absolute of matrix
1482 *
1483 * @return Absolute of matrix
1484 *
1485 * Returns matrix where all elements of the matrix have been replaced by
1486 * their absolute values.
1487 ***************************************************************************/
1488GMatrixSparse GMatrixSparse::abs(void) const
1489{
1490 // Copy matrix
1491 GMatrixSparse matrix = *this;
1492
1493 // Fill pending element
1494 matrix.fill_pending();
1495
1496 // Take the absolute value of all matrix elements
1497 double* ptr = matrix.m_data;
1498 for (int i = 0; i < matrix.m_elements; ++i, ++ptr) {
1499 *ptr = std::abs(*ptr);
1500 }
1501
1502 // Return matrix
1503 return matrix;
1504}
1505
1506
1507/***********************************************************************//**
1508 * @brief Returns fill of matrix
1509 *
1510 * The fill of a matrix is defined as the number non-zero elements devided
1511 * by the number of total elements. By definiton, the fill is comprised
1512 * in the interval [0,..,1]. The fill of an undefined matrix is defined to
1513 * be 0.
1514 ***************************************************************************/
1515double GMatrixSparse::fill(void) const
1516{
1517 // Initialise result
1518 double result = 0.0;
1519
1520 // Compute matrix size
1521 int size = m_rows*m_cols;
1522
1523 // Continue only if matrix has elements
1524 if (size > 0) {
1525
1526 // Determine number of elements in matrix
1527 int num = (m_fill_val == 0.0) ? m_elements : m_elements + 1;
1528
1529 // Compute fill
1530 result = double(num) / double(size);
1531
1532 } // endif: there were elements in matrix
1533
1534 // Return fill
1535 return result;
1536}
1537
1538
1539/***********************************************************************//**
1540 * @brief Return minimum matrix element
1541 ***************************************************************************/
1542double GMatrixSparse::min(void) const
1543{
1544 // Initialise minimum with fill value
1545 double result = m_fill_val;
1546
1547 // Search all elements for the smallest one
1548 for (int i = 0; i < m_elements; ++i) {
1549 if (m_data[i] < result) {
1550 result = m_data[i];
1551 }
1552 }
1553
1554 // If minimum is > 0.0 and there are zero elements then set the minimum
1555 // to 0.0
1556 if ((result > 0.0) && (m_elements < (m_rows*m_cols))) {
1557 result = 0.0;
1558 }
1559
1560 // Return result
1561 return result;
1562}
1563
1564
1565/***********************************************************************//**
1566 * @brief Return maximum matrix element
1567 ***************************************************************************/
1568double GMatrixSparse::max(void) const
1569{
1570 // Initialise maximum with fill value
1571 double result = m_fill_val;
1572
1573 // Search all elements for the largest one
1574 for (int i = 0; i < m_elements; ++i) {
1575 if (m_data[i] > result) {
1576 result = m_data[i];
1577 }
1578 }
1579
1580 // If maximum is < 0.0 and there are zero elements then set the maximum
1581 // to 0.0
1582 if ((result < 0.0) && (m_elements < (m_rows*m_cols))) {
1583 result = 0.0;
1584 }
1585
1586 // Return result
1587 return result;
1588}
1589
1590
1591/***********************************************************************//**
1592 * @brief Sum matrix elements
1593 ***************************************************************************/
1594double GMatrixSparse::sum(void) const
1595{
1596 // Initialise matrix sum with fill value
1597 double result = m_fill_val;
1598
1599 // Add all elements
1600 for (int i = 0; i < m_elements; ++i) {
1601 result += m_data[i];
1602 }
1603
1604 // Return result
1605 return result;
1606}
1607
1608
1609/***********************************************************************//**
1610 * @brief Return Cholesky decomposition
1611 *
1612 * @param[in] compress Use zero-row/column compression (defaults to true).
1613 * @return Cholesky decomposition of matrix
1614 *
1615 * Returns the Cholesky decomposition of a sparse matrix. The decomposition
1616 * is stored within a GMatrixSparse object.
1617 ***************************************************************************/
1618GMatrixSparse GMatrixSparse::cholesky_decompose(const bool& compress) const
1619{
1620 // Create copy of matrix
1621 GMatrixSparse matrix = *this;
1622
1623 // Save original matrix size
1624 int matrix_rows = matrix.m_rows;
1625 int matrix_cols = matrix.m_cols;
1626
1627 // Delete any existing symbolic and numeric analysis object and reset
1628 // pointers
1629 if (matrix.m_symbolic != NULL__null) delete matrix.m_symbolic;
1630 if (matrix.m_numeric != NULL__null) delete matrix.m_numeric;
1631 matrix.m_symbolic = NULL__null;
1632 matrix.m_numeric = NULL__null;
1633
1634 // Allocate symbolic analysis object
1635 GSparseSymbolic* symbolic = new GSparseSymbolic();
1636
1637 // Declare numeric analysis object. We don't allocate one since we'll
1638 // throw it away at the end of the function (the L matrix will be copied
1639 // in this object)
1640 GSparseNumeric numeric;
1641
1642 // Fill pending element into matrix
1643 matrix.fill_pending();
1644
1645 // Remove rows and columns containing only zeros if matrix compression
1646 // has been selected
1647 if (compress) {
1648 matrix.remove_zero_row_col();
1649 }
1650
1651 // Ordering an symbolic analysis of matrix. This sets up an array 'pinv'
1652 // which contains the fill-in reducing permutations
1653 symbolic->cholesky_symbolic_analysis(1, matrix);
1654
1655 // Store symbolic pointer in sparse matrix object
1656 matrix.m_symbolic = symbolic;
1657
1658 // Perform numeric Cholesky decomposition
1659 numeric.cholesky_numeric_analysis(matrix, *symbolic);
1660
1661 // Copy L matrix into this object
1662 matrix.free_elements(0, matrix.m_elements);
1663 matrix.alloc_elements(0, numeric.m_L->m_elements);
1664 for (int i = 0; i < matrix.m_elements; ++i) {
1665 matrix.m_data[i] = numeric.m_L->m_data[i];
1666 matrix.m_rowinx[i] = numeric.m_L->m_rowinx[i];
1667 }
1668 for (int col = 0; col <= matrix.m_cols; ++col) {
1669 matrix.m_colstart[col] = numeric.m_L->m_colstart[col];
1670 }
1671
1672 // Insert zero rows and columns if they have been removed previously.
1673 if (compress) {
1674 matrix.insert_zero_row_col(matrix_rows, matrix_cols);
1675 }
1676
1677 // Return matrix
1678 return matrix;
1679}
1680
1681
1682/***********************************************************************//**
1683 * @brief Cholesky solver
1684 *
1685 * @param[in] vector Solution vector.
1686 * @param[in] compress Request matrix compression.
1687 *
1688 * @exception GException::matrix_vector_mismatch
1689 * Matrix and vector do not match.
1690 * @exception GException::matrix_not_factorised
1691 * Matrix has not been factorised.
1692 *
1693 * Solves the linear equation A*x=b using a Cholesky decomposition of A.
1694 * This function is to be applied on a GMatrixSparse matrix for which a
1695 * Choleksy factorization has been produced using 'cholesky_decompose'.
1696 ***************************************************************************/
1697GVector GMatrixSparse::cholesky_solver(const GVector& vector,
1698 const bool& compress) const
1699{
1700 // Dump header
1701 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1702 std::cout << "GMatrixSparse::cholesky_solver" << std::endl;
1703 std::cout << " Input vector .....: " << vector << std::endl;
1704 #endif
1705
1706 // Raise an exception if the matrix and vector dimensions are incompatible
1707 if (m_rows != vector.size()) {
1708 throw GException::matrix_vector_mismatch(G_CHOL_SOLVE"GMatrixSparse::cholesky_solver(GVector&, bool)", vector.size(),
1709 m_rows, m_cols);
1710 }
1711
1712 // Raise an exception if there is no symbolic pointer
1713 if (!m_symbolic) {
1714 throw GException::matrix_not_factorised(G_CHOL_SOLVE"GMatrixSparse::cholesky_solver(GVector&, bool)",
1715 "Cholesky decomposition");
1716 }
1717
1718 // Raise an exception if there is no permutation
1719 if (!m_symbolic->m_pinv) {
1720 throw GException::matrix_not_factorised(G_CHOL_SOLVE"GMatrixSparse::cholesky_solver(GVector&, bool)",
1721 "Cholesky decomposition");
1722 }
1723
1724 // Flag row and column compression
1725 int row_compressed = (m_rowsel != NULL__null && m_num_rowsel < m_rows);
1726 int col_compressed = (m_colsel != NULL__null && m_num_colsel < m_cols);
1727
1728 // Decide if we need a compression algorithm or not
1729 int no_zero = !(compress && (row_compressed || col_compressed));
1730
1731 // Allocate vector for permutation and result vector
1732 GVector result(m_cols);
1733
1734 // Setup pointers to L matrix and x vector
1735 int* Lp = m_colstart;
1736 int* Li = m_rowinx;
1737 double* Lx = m_data;
1738
1739 // Case A: no zero-row/col compression needed
1740 if (no_zero) {
1741
1742 // Declare working vector
1743 GVector x(m_rows);
1744
1745 // Perform inverse vector permutation
1746 for (int i = 0; i < vector.size(); ++i) {
1747 x[m_symbolic->m_pinv[i]] = vector[i];
1748 }
1749
1750 // Inplace solve L\x=x
1751 for (int col = 0; col < m_cols; ++col) { // loop over columns
1752 x[col] /= Lx[Lp[col]]; // divide by diag.
1753 for (int p = Lp[col]+1; p < Lp[col+1]; p++) // loop over elements
1754 x[Li[p]] -= Lx[p] * x[col];
1755 }
1756
1757 // Inplace solve L'\x=x
1758 for (int col = m_cols-1; col >= 0; --col) { // loop over columns
1759 for (int p = Lp[col]+1; p < Lp[col+1]; p++) // loop over elements
1760 x[col] -= Lx[p] * x[Li[p]];
1761 x[col] /= Lx[Lp[col]];
1762 }
1763
1764 // Perform vector permutation
1765 for (int i = 0; i < m_cols; ++i) {
1766 result[i] = x[m_symbolic->m_pinv[i]];
1767 }
1768
1769 } // endif: Case A
1770
1771 // Case B: zero-row/column compression requested
1772 else {
1773
1774 // Allocate row and column mapping arrays
1775 int* row_map = new int[m_rows];
1776 int* col_map = new int[m_cols];
1777
1778 // Setup row mapping array that maps original matrix rows into compressed
1779 // matrix rows. An entry of -1 indicates that the row should be dropped.
1780 // If no selection exists then setup an identity map.
1781 if (row_compressed) {
1782 for (int row = 0; row < m_rows; ++row) {
1783 row_map[row] = -1;
1784 }
1785 for (int c_row = 0; c_row < m_num_rowsel; ++c_row) {
1786 row_map[m_rowsel[c_row]] = c_row;
1787 }
1788 }
1789 else {
1790 for (int row = 0; row < m_rows; ++row) {
1791 row_map[row] = row;
1792 }
1793 }
1794 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1795 std::cout << " Row mapping ......:";
1796 for (int row = 0; row < m_rows; ++row) {
1797 std::cout << " " << row_map[row];
1798 }
1799 std::cout << std::endl;
1800 #endif
1801
1802 // Setup column mapping array that maps original matrix column into compressed
1803 // matrix columns. An entry of -1 indicates that the column should be dropped
1804 // If no selection exists then setup an identity map.
1805 if (col_compressed) {
1806 for (int col = 0; col < m_cols; ++col) {
1807 col_map[col] = -1;
1808 }
1809 for (int c_col = 0; c_col < m_num_colsel; ++c_col) {
1810 col_map[m_colsel[c_col]] = c_col;
1811 }
1812 }
1813 else {
1814 for (int col = 0; col < m_cols; ++col) {
1815 col_map[col] = col;
1816 }
1817 }
1818 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1819 std::cout << " Column mapping ...:";
1820 for (int col = 0; col < m_cols; ++col) {
1821 std::cout << " " << col_map[col];
1822 }
1823 std::cout << std::endl;
1824 #endif
1825
1826 // Declare working vector
1827 GVector x(row_compressed ? m_num_rowsel : m_rows);
1828
1829 // Compress input vector v -> c_v if required
1830 if (m_rowsel != NULL__null && m_num_rowsel < m_rows) {
1831 for (int c_row = 0; c_row < m_num_rowsel; ++c_row) {
1832 x[c_row] = vector[m_rowsel[c_row]];
1833 }
1834 }
1835 else {
1836 x = vector;
1837 }
1838 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1839 std::cout << " Compressed vector : " << x << std::endl;
1840 #endif
1841
1842 // Perform inverse permutation
1843 x = iperm(x, m_symbolic->m_pinv);
1844 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1845 std::cout << " Permutated vector : " << x << std::endl;
1846 #endif
1847
1848 // Inplace solve L\x=x. The column and row maps are just use to see which
1849 // columns or rows should be skipped in the calculations.
1850 for (int col = 0; col < m_cols; ++col) { // loop over columns
1851 int c_col = col_map[col];
1852 if (c_col >= 0) { // use only non-zero cols
1853 x[c_col] /= Lx[Lp[col]]; // divide by diag.
1854 for (int p = Lp[col]+1; p < Lp[col+1]; p++) { // loop over elements
1855 int c_row = row_map[Li[p]];
1856 if (c_row >= 0) { // use only non-zero rows
1857 x[c_row] -= Lx[p] * x[c_col];
1858 }
1859 }
1860 }
1861 }
1862 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1863 std::cout << " Solve Lx=x .......: " << x << std::endl;
1864 #endif
1865
1866 // Inplace solve L'\x=x. The column and row maps are just use to see which
1867 // columns or rows should be skipped in the calculations.
1868 for (int col = m_cols-1; col >= 0; --col) { // loop over columns
1869 int c_col = col_map[col];
1870 if (c_col >= 0) { // use only non-zero cols
1871 for (int p = Lp[col]+1; p < Lp[col+1]; p++) { // loop over elements
1872 int c_row = row_map[Li[p]];
1873 if (c_row >= 0) { // use only non-zero rows
1874 x[c_col] -= Lx[p] * x[c_row];
1875 }
1876 }
1877 x[c_col] /= Lx[Lp[col]];
1878 }
1879 }
1880 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1881 std::cout << " Solve L'x=x ......: " << x << std::endl;
1882 #endif
1883
1884 // Perform vector permutation
1885 x = perm(x, m_symbolic->m_pinv);
1886 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1887 std::cout << " Permutated vector : " << x << std::endl;
1888 #endif
1889
1890 // If column compression has been performed the expand the result vector
1891 // accordingly
1892 if (m_colsel != NULL__null && m_num_colsel < m_cols) {
1893 for (int c_col = 0; c_col < m_num_colsel; ++c_col) {
1894 result[m_colsel[c_col]] = x[c_col];
1895 }
1896 }
1897 else {
1898 result = x;
1899 }
1900 #if defined(G_DEBUG_SPARSE_COMPRESSION)
1901 std::cout << " Restored vector ..: " << result << std::endl;
1902 #endif
1903
1904 // Free mapping arrays
1905 delete [] row_map;
1906 delete [] col_map;
1907
1908 } // endelse: Case B
1909
1910 // Return result vector
1911 return result;
1912}
1913
1914
1915/***********************************************************************//**
1916 * @brief Invert matrix using a Cholesky decomposition
1917 *
1918 * @param[in] compress Use zero-row/column compression (defaults to true).
1919 * @return Inverted matrix.
1920 *
1921 * Inverts the matrix using a Cholesky decomposition.
1922 ***************************************************************************/
1923GMatrixSparse GMatrixSparse::cholesky_invert(const bool& compress) const
1924{
1925 // Generate Cholesky decomposition of matrix
1926 GMatrixSparse decomposition = cholesky_decompose(compress);
1927
1928 // Allocate result matrix and unit vector
1929 GMatrixSparse matrix(m_rows, m_cols);
1930 GVector unit(m_rows);
1931
1932 // Column-wise solving of the problem
1933 for (int col = 0; col < m_cols; ++col) {
1934
1935 // Set unit vector
1936 unit[col] = 1.0;
1937
1938 // Solve for column
1939 GVector x = decomposition.cholesky_solver(unit, compress);
1940
1941 // Insert column in matrix
1942 matrix.column(col, x);
1943
1944 // Clear unit vector for next round
1945 unit[col] = 0.0;
1946
1947 }
1948
1949 // Return matrix
1950 return matrix;
1951}
1952
1953
1954/***********************************************************************//**
1955 * @brief Print matrix
1956 *
1957 * @param[in] chatter Chattiness (defaults to NORMAL).
1958 * @return String containing matrix information
1959 ***************************************************************************/
1960std::string GMatrixSparse::print(const GChatter& chatter) const
1961{
1962 // Initialise result string
1963 std::string result;
1964
1965 // Continue only if chatter is not silent
1966 if (chatter != SILENT) {
1967
1968 // Determine number of elements
1969 int nonzero;
1970 int elements = m_elements;
1971 if (m_fill_val == 0.0) {
1972 nonzero = (m_colstart != NULL__null) ? m_colstart[m_cols] : 0;
1973 }
1974 else {
1975 nonzero = (m_colstart != NULL__null) ? m_colstart[m_cols]+1 : 0;
1976 elements++;
1977 }
1978
1979 // Append header
1980 result.append("=== GMatrixSparse ===");
1981
1982 // Append information
1983 result.append("\n"+gammalib::parformat("Number of rows"));
1984 result.append(gammalib::str(m_rows));
1985 if (m_rowsel != NULL__null) {
1986 result.append(" (compressed "+gammalib::str(m_num_rowsel)+")");
1987 }
1988 result.append("\n"+gammalib::parformat("Number of columns"));
1989 result.append(gammalib::str(m_cols));
1990 if (m_colsel != NULL__null) {
1991 result.append(" (compressed "+gammalib::str(m_num_colsel)+")");
1992 }
1993 result.append("\n"+gammalib::parformat("Number of nonzero elements"));
1994 result.append(gammalib::str(nonzero));
1995 if (m_fill_val != 0.0) {
1996 result.append("\n"+gammalib::parformat("Pending element"));
1997 result.append("("+gammalib::str(m_fill_row)+","+gammalib::str(m_fill_col)+")=");
1998 result.append(gammalib::str(m_fill_val));
1999 }
2000 result.append("\n"+gammalib::parformat("Number of allocated cells"));
2001 result.append(gammalib::str(m_alloc));
2002 result.append("\n"+gammalib::parformat("Memory block size"));
2003 result.append(gammalib::str(m_mem_block));
2004 result.append("\n"+gammalib::parformat("Sparse matrix fill"));
2005 result.append(gammalib::str(fill()));
2006 result.append("\n"+gammalib::parformat("Pending element"));
2007 result.append(gammalib::str(m_fill_val));
2008 result.append("\n"+gammalib::parformat("Fill stack size"));
2009 result.append(gammalib::str(m_stack_size));
2010 if (m_stack_data == NULL__null) {
2011 result.append(" (none)");
2012 }
2013
2014 // Append elements and compression schemes
2015 result.append(print_elements(chatter));
2016 result.append(print_row_compression(chatter));
2017 result.append(print_col_compression(chatter));
2018
2019 // VERBOSE
2020 if (chatter >= VERBOSE) {
2021
2022 // Append symbolic decomposition if available
2023 //if (m_symbolic != NULL) {
2024 // result.append(m_symbolic->print(chatter));
2025 //}
2026
2027 // Append numeric decomposition if available
2028 //if (m_numeric != NULL) {
2029 // result.append(m_numeric->print(chatter));
2030 //}
2031
2032 } // endif: chatter was verbose
2033
2034 } // endif: chatter was not silent
2035
2036 // Return result
2037 return result;
2038}
2039
2040
2041/***********************************************************************//**
2042 * @brief Initialises matrix filling stack
2043 *
2044 * @param[in] size Stack size.
2045 * @param[in] entries Maximum number of entries.
2046 *
2047 * The matrix filling stack is used to allow for a fast column-wise filling
2048 * of a sparse matrix. Columns are successively appended to a stack which is
2049 * regularily flushed when it is full. This reduces memory copies and
2050 * movements and increases filling speed.
2051 ***************************************************************************/
2052void GMatrixSparse::stack_init(const int& size, const int& entries)
2053{
2054 // Free exisiting stack
2055 free_stack_members();
2056
2057 // Initialise stack members
2058 init_stack_members();
2059 m_stack_max_entries = (entries > 0) ? entries : m_cols;
2060 m_stack_size = (size > 0) ? size : G_SPARSE_MATRIX_DEFAULT_STACK_SIZE512;
2061
2062 // Allocate stack memory. Raise an exception if allocation fails
2063 m_stack_colinx = new int[m_stack_max_entries];
2064 m_stack_start = new int[m_stack_max_entries+1];
2065 m_stack_data = new double[m_stack_size];
2066 m_stack_rowinx = new int[m_stack_size];
2067 m_stack_work = new int[m_cols];
2068 m_stack_rows = new int[m_cols];
2069 m_stack_values = new double[m_cols];
2070
2071 // Initialise next free stack location to the first stack element
2072 m_stack_start[0] = 0;
2073
2074 // Return
2075 return;
2076}
2077
2078
2079/***********************************************************************//**
2080 * @brief Push a vector column on the matrix stack
2081 *
2082 * @param[in] vector Vector.
2083 * @param[in] col Column index (starting from 0).
2084 *
2085 * @exception GException::out_of_range
2086 * Invalid column index specified.
2087 * @exception GException::matrix_vector_mismatch
2088 * Matrix dimension mismatches the vector size.
2089 *
2090 * Adds the contect of a vector to the matrix stack. This method is
2091 * identical to the GMatrixSparse::stack_push_column method that uses a
2092 * compressed array, yet it takes a full vector. Internal working arrays
2093 * are used to convert the full column vector in a compressed array and to
2094 * hand it over to the compressed array version.
2095 ***************************************************************************/
2096int GMatrixSparse::stack_push_column(const GVector& vector, const int& col)
2097{
2098 // Initialise number of non-zero elements
2099 int non_zero = 0;
2100
2101 // Compress vector in the buffer and set-up row index array
2102 for (int i = 0; i < vector.size(); ++i) {
2103 if (vector[i] != 0.0) {
2104 m_stack_values[non_zero] = vector[i];
2105 m_stack_rows[non_zero] = i;
2106 non_zero++;
2107 }
2108 }
2109
2110 // Hand over to compressed array method
2111 int remaining = stack_push_column(m_stack_values, m_stack_rows, non_zero, col);
2112
2113 // Return remaining number of elements
2114 return remaining;
2115}
2116
2117
2118/***********************************************************************//**
2119 * @brief Push a compressed array on the matrix stack
2120 *
2121 * @param[in] values Compressed array.
2122 * @param[in] rows Row indices of array.
2123 * @param[in] number Number of elements in array.
2124 * @param[in] col Column index (starting from 0).
2125 *
2126 * @exception GException::out_of_range
2127 * Invalid column index specified.
2128 * @exception GException::matrix_vector_mismatch
2129 * Matrix dimension mismatches the vector size.
2130 *
2131 * The method puts new data on the stack while assuring that each column
2132 * is present only once. If an already existing column is encountered
2133 * the data from the existing and new column are mixed and put into a new
2134 * entry one the stack; the old entry is signalled as obsolete.
2135 *
2136 * On return the method indicates the number of elements in the input
2137 * array that have not been put onto the stack (due to memory limits).
2138 * This value can be either '0' (i.e. all elements have been put on the
2139 * stack) or 'number' (i.e. none of the elements have been put on the
2140 * stack). Columns are not partially put onto the stack.
2141 ***************************************************************************/
2142int GMatrixSparse::stack_push_column(const double* values, const int* rows,
2143 const int& number, const int& col)
2144{
2145 // Initialise return value
2146 int remaining = number;
2147
2148 // Single loop for common exit point
2149 do {
2150
2151 // If the array is empty there is nothing to do
2152 if (!values || !rows || (number < 1)) {
2153 continue;
2154 }
2155
2156 // If there is no stack or the stack can not hold the requested
2157 // number of elements then report number of array elements to the
2158 // caller
2159 if (m_stack_data == NULL__null || number > m_stack_size) {
2160 continue;
2161 }
2162
2163 // Raise an exception if the column index is invalid
2164 #if defined(G_RANGE_CHECK1)
2165 if (col < 0 || col >= m_cols) {
2166 throw GException::out_of_range(G_STACK_PUSH"GMatrixSparse::stack_push_column(double*, int*, int&," " int&)", col, 0, m_cols-1);
2167 }
2168 #endif
2169
2170 // Raise an exception if the matrix and vector dimensions are
2171 // incompatible
2172 if (rows[number-1] >= m_rows) {
2173 throw GException::matrix_vector_mismatch(G_STACK_PUSH"GMatrixSparse::stack_push_column(double*, int*, int&," " int&)", rows[number-1],
2174 m_rows, m_cols);
2175 }
2176
2177 // Debug header
2178 #if defined(G_DEBUG_SPARSE_STACK_PUSH)
2179 std::cout << "GMatrixSparse::stack_push_column(v, i, n, col=" << col << ")";
2180 std::cout << std::endl;
2181 std::cout << " Data to push on stack ...:";
2182 for (int i = 0; i < number; ++i) {
2183 std::cout << " " << values[i];
2184 }
2185 std::cout << std::endl;
2186 std::cout << " Row indices of data .....:";
2187 for (int i = 0; i < number; ++i) {
2188 std::cout << " " << rows[i];
2189 }
2190 std::cout << std::endl;
2191 #endif
2192
2193 // If the stack is full then flush it before pushing the array onto it.
2194 // There are 2 conditions that may fill the stack: all entries are
2195 // occupied or there is not enough space to hold more elements
2196 if ((m_stack_entries >= m_stack_max_entries) ||
2197 (number >= (m_stack_size - m_stack_start[m_stack_entries]))) {
2198 stack_flush();
2199 }
2200
2201 // If the specified column is already on the stack then mix new column
2202 // with old one and invalidate old column (by setting its column index
2203 // to -1).
2204 // Since we loop here over all stack entries we don't want to have too
2205 // many entries in the stack. So don't set the maximum number of stack
2206 // entries too large ...
2207 for (int entry = 0; entry < m_stack_entries; ++entry) {
2208
2209 // Is the specified column already on the stack?
2210 if (col == m_stack_colinx[entry]) {
2211
2212 // Get start and stop indices of existing column in stack
2213 int i_start = m_stack_start[entry];
2214 int i_stop = m_stack_start[entry+1];
2215
2216 // Allocate variables to hold mixing results
2217 int num_1; // Number of elements that are only in stack column
2218 int num_2; // Number of elements that are only in new column
2219 int num_mix; // Number of elements that are in both columns
2220
2221 // Determine how many elements are requested to hold the combined
2222 // column
2223 mix_column_prepare(&(m_stack_rowinx[i_start]), i_stop-i_start,
2224 rows, number, &num_1, &num_2, &num_mix);
2225 int num_request = num_1 + num_2 + num_mix;
2226
2227 // If there is not enough space in the stack to hold the combined
2228 // column we flush the stack and exit the loop now. In this case
2229 // the new column is put as the first column in the empty (flushed
2230 // stack)
2231 if (num_request >= (m_stack_size - m_stack_start[m_stack_entries])) {
2232 stack_flush();
2233 break;
2234 }
2235
2236 // There is enough space, so combine both columns into a fresh one
2237 int inx = m_stack_start[m_stack_entries];
2238 int num_new;
2239 mix_column(&(m_stack_data[i_start]), &(m_stack_rowinx[i_start]),
2240 i_stop-i_start,
2241 values, rows, number,
2242 &(m_stack_data[inx]), &(m_stack_rowinx[inx]), &num_new);
2243
2244 // Store entry information and initialise start of next entry
2245 m_stack_colinx[m_stack_entries] = col; // Store column index for entry
2246 m_stack_entries++; // Increase the # of entries
2247 m_stack_start[m_stack_entries] = inx + num_new; // Set start pointer for next entry
2248
2249 // Invalidate old column
2250 m_stack_colinx[entry] = -1;
2251
2252 // Fall through to end
2253 remaining = 0;
2254 break;
2255
2256 } // endif: we found an existing column
2257 } // endfor: looped over all columns
2258
2259 // If column has already been inserted then fall through ...
2260 if (remaining == 0) {
2261 continue;
2262 }
2263
2264 // Otherwise, push the array on the stack
2265 int inx = m_stack_start[m_stack_entries];
2266 for (int i = 0; i < number; ++i) {
2267 m_stack_data[inx] = values[i];
2268 m_stack_rowinx[inx] = rows[i];
2269 inx++;
2270 }
2271
2272 // Store entry information and initialise start of next entry
2273 m_stack_colinx[m_stack_entries] = col; // Store column index for entry
2274 m_stack_entries++; // Increase the # of entries
2275 m_stack_start[m_stack_entries] = inx; // Set start pointer for next entry
2276
2277 // Signal success
2278 remaining = 0;
2279
2280 } while (0); // End of main do-loop
2281
2282 // Debug: show stack information
2283 #if defined(G_DEBUG_SPARSE_STACK_PUSH)
2284 std::cout << " Number of stack entries .: " << m_stack_entries << std::endl;
2285 std::cout << " Stack entry columns .....:";
2286 for (int i = 0; i < m_stack_entries; ++i) {
2287 std::cout << " " << m_stack_colinx[i];
2288 }
2289 std::cout << std::endl;
2290 std::cout << " Stack entry starts ......:";
2291 for (int i = 0; i < m_stack_entries; ++i) {
2292 std::cout << " " << m_stack_start[i];
2293 }
2294 std::cout << " (next at " << m_stack_start[m_stack_entries] << ")" << std::endl;
2295 std::cout << " Stack data ..............:";
2296 for (int i = 0; i < m_stack_start[m_stack_entries]; ++i) {
2297 std::cout << " " << m_stack_data[i];
2298 }
2299 std::cout << std::endl;
2300 std::cout << " Stack rows ..............:";
2301 for (int i = 0; i < m_stack_start[m_stack_entries]; ++i) {
2302 std::cout << " " << m_stack_rowinx[i];
2303 }
2304 std::cout << std::endl;
2305 #endif
2306
2307 // Return remaining number of elements
2308 return remaining;
2309}
2310
2311
2312/***********************************************************************//**
2313 * @brief Flush matrix stack
2314 *
2315 * Adds the stack to the actual matrix. First, the total number of matrix
2316 * elements is determined. Then new memory is allocated to hold all
2317 * elements. Finally, all elements are filled into a working array that is
2318 * then compressed into the matrix.
2319 *
2320 * The method uses the internal working array m_stack_work.
2321 *
2322 * NOTE: The present algorithm assumes that each column occurs only once
2323 * in the stack!
2324 ***************************************************************************/
2325void GMatrixSparse::stack_flush(void)
2326{
2327 // Do nothing if there is no stack
2328 if (m_stack_data == NULL__null) {
2329 return;
2330 }
2331
2332 // Fill pending value
2333 fill_pending();
2334
2335 // Debug header
2336 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2337 std::cout << "GMatrixSparse::stack_flush" << std::endl;
2338 std::cout << " Number of stack entries .: " << m_stack_entries << std::endl;
2339 std::cout << " Number of stack elements : " << m_stack_start[m_stack_entries] << std::endl;
2340 std::cout << " Number of matrix elements: " << m_elements << std::endl;
2341 #endif
2342
2343 // Use stack working array to flag all columns that exist already in the
2344 // matrix by 1 and all non-existing columns by 0
2345 for (int col = 0; col < m_cols; ++col) {
2346 if (m_colstart[col] < m_colstart[col+1]) {
2347 m_stack_work[col] = 1;
2348 }
2349 else {
2350 m_stack_work[col] = 0;
2351 }
2352 }
2353
2354 // Initialise some element counters
2355 int new_elements = 0;
2356 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2357 int num_matrix = 0; // Number of elements only in matrix
2358 int num_stack = 0; // Number of elements only on stack
2359 int num_both = 0; // Number of elements in matrix and on stack
2360 #endif
2361
2362 // Loop over all stack entries and gather the number of elements that are
2363 // new with respect to the initial matrix. For each column set a flag with
2364 // the following meanings:
2365 // -(entry+2): Column exists and is also found in stack=entry
2366 // +(entry+2): Column exists in stack=entry only
2367 // 1: Column exists in matrix only
2368 // 0: Column does neither exist in matrix or stack
2369 for (int entry = 0; entry < m_stack_entries; ++entry) {
2370
2371 // Get column for actual entry
2372 int col = m_stack_colinx[entry];
2373
2374 // Consider only valid entries
2375 if (col >= 0) {
2376
2377 // If column exists already in matrix then determine total number
2378 // of additional elements
2379 if (m_stack_work[col] == 1) {
2380
2381 // Flag that this column is a mixed column
2382 m_stack_work[col] = -(entry+2);
2383
2384 // Setup index boundaries
2385 int i_start = m_colstart[col];
2386 int i_stop = m_colstart[col+1];
2387 int k_start = m_stack_start[entry];
2388 int k_stop = m_stack_start[entry+1];
2389
2390 // Allocate output counters
2391 int num_1;
2392 int num_2;
2393 int num_mix;
2394
2395 // Analyse column mixing
2396 mix_column_prepare(&(m_rowinx[i_start]), i_stop-i_start,
2397 &(m_stack_rowinx[k_start]), k_stop-k_start,
2398 &num_1, &num_2, &num_mix);
2399
2400 // Update element counters
2401 new_elements += num_2;
2402 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2403 num_matrix += num_1;
2404 num_stack += num_2;
2405 num_both += num_mix;
2406 #endif
2407
2408 } // endif: column existed in the matrix
2409
2410 // If column did not exists in the matrix then consider all
2411 // elements as new
2412 else {
2413 m_stack_work[col] = (entry+2);
2414 new_elements += (m_stack_start[entry+1] - m_stack_start[entry]);
2415 }
2416 } // endif: entry was valid
2417 } // endfor: looped over all entries
2418 int elements = m_elements + new_elements;
2419
2420 // Dump number of elements in new matrix
2421 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2422 std::cout << " New elements ............: " << new_elements << std::endl;
2423 #endif
2424
2425 // Allocate memory for new matrix (always keep some elbow room)
2426 m_alloc = elements + m_mem_block;
2427 double* new_data = new double[m_alloc];
2428 int* new_rowinx = new int[m_alloc];
2429
2430 // Fill new matrix. For this purpose we loop over all matrix columns
2431 // and perform the operation that was identified in the previous scan
2432 int index = 0;
2433 for (int col = 0; col < m_cols; ++col) {
2434
2435 // If column does not exist then skip
2436 if (m_stack_work[col] == 0) {
2437 m_colstart[col] = index;
2438 continue;
2439 }
2440
2441 // If column exists in the matrix but not in the stack we copy the
2442 // existing column into the new matrix
2443 if (m_stack_work[col] == 1) {
2444 for (int i = m_colstart[col]; i < m_colstart[col+1]; ++i) {
2445 new_data[index] = m_data[i];
2446 new_rowinx[index] = m_rowinx[i];
2447 index++;
2448 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2449 num_matrix++;
2450 #endif
2451 }
2452 }
2453
2454 // If column is new (i.e. it did not exist in the matrix before)
2455 // we copy the stack into the new matrix
2456 else if (m_stack_work[col] > 1) {
2457 int entry = m_stack_work[col] - 2;
2458 for (int i = m_stack_start[entry]; i < m_stack_start[entry+1]; ++i) {
2459 new_data[index] = m_stack_data[i];
2460 new_rowinx[index] = m_stack_rowinx[i];
2461 index++;
2462 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2463 num_stack++;
2464 #endif
2465 }
2466 }
2467
2468 // If column exists in the matrix and in the stack then we have to mix both
2469 else {
2470
2471 // Get stack entry for column mix
2472 int entry = -(m_stack_work[col] + 2);
2473
2474 // Setup index boundaries
2475 int i_start = m_colstart[col];
2476 int i_stop = m_colstart[col+1];
2477 int k_start = m_stack_start[entry];
2478 int k_stop = m_stack_start[entry+1];
2479
2480 // Allocate output counter
2481 int num;
2482
2483 // Perform column mixing
2484 mix_column(&(m_data[i_start]), &(m_rowinx[i_start]), i_stop-i_start,
2485 &(m_stack_data[k_start]), &(m_stack_rowinx[k_start]), k_stop-k_start,
2486 &(new_data[index]), &(new_rowinx[index]), &num);
2487
2488 // Increment element index
2489 index += num;
2490
2491 } // endelse: column mixing required
2492
2493 // Store actual index in column start array
2494 m_colstart[col] = index;
2495
2496 } // endfor: looped over all columns
2497
2498 // Dump number of elements in new matrix after addition
2499 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2500 std::cout << " Added elements ..........: " << index << " (should be " << elements << ")" << std::endl;
2501 std::cout << " - Matrix only ...........: " << num_matrix << std::endl;
2502 std::cout << " - Stack only ............: " << num_stack << std::endl;
2503 std::cout << " - Matrix & Stack ........: " << num_both << std::endl;
2504 #endif
2505
2506 // Correct columns start array
2507 for (int col = m_cols; col > 0; --col) {
2508 m_colstart[col] = m_colstart[col-1];
2509 }
2510 m_colstart[0] = 0;
2511
2512 // Delete old matrix memory
2513 if (m_data != NULL__null) delete [] m_data;
2514 if (m_rowinx != NULL__null) delete [] m_rowinx;
2515
2516 // Update pointers to new memory and update element counter
2517 m_data = new_data;
2518 m_rowinx = new_rowinx;
2519 m_elements = elements;
2520
2521 // Stack is empty now, so reset stack counters
2522 m_stack_entries = 0;
2523 m_stack_start[0] = 0;
2524
2525 // Return
2526 return;
2527}
2528
2529
2530/***********************************************************************//**
2531 * @brief Destroy matrix stack
2532 *
2533 * Flush and destroy matrix stack
2534 ***************************************************************************/
2535void GMatrixSparse::stack_destroy(void)
2536{
2537 // Flush stack first
2538 stack_flush();
2539
2540 // Free stack members
2541 free_stack_members();
2542
2543 // Initialise stack (no entries)
2544 init_stack_members();
2545
2546 // Return
2547 return;
2548}
2549
2550
2551/*==========================================================================
2552 = =
2553 = Private methods =
2554 = =
2555 ==========================================================================*/
2556
2557/***********************************************************************//**
2558 * @brief Initialise class mambers
2559 ***************************************************************************/
2560void GMatrixSparse::init_members(void)
2561{
2562 // Initialise sparse matrix members
2563 m_rowinx = NULL__null;
5
Null pointer value stored to 'C.m_rowinx'
2564 m_mem_block = G_SPARSE_MATRIX_DEFAULT_MEM_BLOCK512;
2565 m_zero = 0.0;
2566 m_fill_val = 0.0;
2567 m_fill_row = 0;
2568 m_fill_col = 0;
2569 m_symbolic = NULL__null;
2570 m_numeric = NULL__null;
2571
2572 // Initialise stack members
2573 init_stack_members();
2574
2575 // Return
2576 return;
2577}
2578
2579
2580/***********************************************************************//**
2581 * @brief Copy class members
2582 *
2583 * @param[in] matrix Matrix to be copied.
2584 ***************************************************************************/
2585void GMatrixSparse::copy_members(const GMatrixSparse& matrix)
2586{
2587 // Copy GMatrixSparse members
2588 m_mem_block = matrix.m_mem_block;
2589 m_zero = matrix.m_zero;
2590 m_fill_val = matrix.m_fill_val;
2591 m_fill_row = matrix.m_fill_row;
2592 m_fill_col = matrix.m_fill_col;
2593
2594 // Copy row indices if they exist
2595 if (matrix.m_rowinx != NULL__null && matrix.m_alloc > 0) {
2596 m_rowinx = new int[matrix.m_alloc];
2597 for (int i = 0; i < matrix.m_elements; ++i) {
2598 m_rowinx[i] = matrix.m_rowinx[i];
2599 }
2600 }
2601
2602 // Clone symbolic decomposition if it exists
2603 if (matrix.m_symbolic != NULL__null) {
2604 m_symbolic = new GSparseSymbolic();
2605 *m_symbolic = *matrix.m_symbolic;
2606 }
2607
2608 // Copy numeric decomposition if it exists
2609 if (matrix.m_numeric != NULL__null) {
2610 m_numeric = new GSparseNumeric();
2611 *m_numeric = *matrix.m_numeric;
2612 }
2613
2614 // Return
2615 return;
2616}
2617
2618
2619/***********************************************************************//**
2620 * @brief Delete class members
2621 ***************************************************************************/
2622void GMatrixSparse::free_members(void)
2623{
2624 // De-allocate only if memory has indeed been allocated
2625 if (m_numeric != NULL__null) delete m_numeric;
2626 if (m_symbolic != NULL__null) delete m_symbolic;
2627 if (m_rowinx != NULL__null) delete [] m_rowinx;
2628
2629 // Properly mark members as free
2630 m_rowinx = NULL__null;
2631 m_symbolic = NULL__null;
2632 m_numeric = NULL__null;
2633
2634 // Free stack members
2635 free_stack_members();
2636
2637 // Return
2638 return;
2639}
2640
2641
2642/***********************************************************************//**
2643 * @brief Allocate matrix
2644 *
2645 * @param[in] rows Number of rows.
2646 * @param[in] columns Number of columns.
2647 * @param[in] elements Number of matrix elements to be physically allocated.
2648 *
2649 * This is the main constructor code that allocates and initialises memory
2650 * for matrix elements.
2651 ***************************************************************************/
2652void GMatrixSparse::alloc_members(const int& rows, const int& columns,
2653 const int& elements)
2654{
2655 // Continue only if rows and columns are valid
2656 if (rows > 0 && columns > 0) {
2657
2658 // Free any existing memory
2659 if (m_colstart != NULL__null) delete [] m_colstart;
2660
2661 // Allocate column start array. This is the only array that we can
2662 // allocate at this time. The other arrays can only be allocated
2663 // during filling of the matrix
2664 m_colstart = new int[columns+1];
2665
2666 // Store (logical) matrix size
2667 m_rows = rows;
2668 m_cols = columns;
2669
2670 // Initialise column start indices to 0
2671 for (int col = 0; col <= m_cols; ++col) {
2672 m_colstart[col] = 0;
2673 }
2674
2675 // Optionally allocate memory for matrix elements
2676 if (elements > 0) {
2677 alloc_elements(0, elements);
2678 }
2679
2680 } // endif: number of elements was positive
2681
2682 // Return
2683 return;
2684}
2685
2686
2687/***********************************************************************//**
2688 * @brief Initialise fill stack
2689 *
2690 * The fill stack is used to fill the sparse matrix without any prior know-
2691 * ledge about the number of location of the non-zero matrix elements.
2692 * Values are first filled into the stack and flushed into the sparse
2693 * matrix once the stack is full.
2694 ***************************************************************************/
2695void GMatrixSparse::init_stack_members(void)
2696{
2697 // Initialise stack members
2698 m_stack_max_entries = 0;
2699 m_stack_size = 0;
2700 m_stack_entries = 0;
2701 m_stack_colinx = NULL__null;
2702 m_stack_start = NULL__null;
2703 m_stack_data = NULL__null;
2704 m_stack_rowinx = NULL__null;
2705 m_stack_work = NULL__null;
2706 m_stack_rows = NULL__null;
2707 m_stack_values = NULL__null;
2708
2709 // Return
2710 return;
2711}
2712
2713
2714/***********************************************************************//**
2715 * @brief Delete fill-stack class members
2716 *
2717 * Deletes all memory that has been allocated using the init_stack_members()
2718 * method.
2719 ***************************************************************************/
2720void GMatrixSparse::free_stack_members(void)
2721{
2722 // Free stack members
2723 if (m_stack_colinx != NULL__null) delete [] m_stack_colinx;
2724 if (m_stack_start != NULL__null) delete [] m_stack_start;
2725 if (m_stack_data != NULL__null) delete [] m_stack_data;
2726 if (m_stack_rowinx != NULL__null) delete [] m_stack_rowinx;
2727 if (m_stack_work != NULL__null) delete [] m_stack_work;
2728 if (m_stack_rows != NULL__null) delete [] m_stack_rows;
2729 if (m_stack_values != NULL__null) delete [] m_stack_values;
2730
2731 // Properly mark members as free
2732 m_stack_colinx = NULL__null;
2733 m_stack_start = NULL__null;
2734 m_stack_data = NULL__null;
2735 m_stack_rowinx = NULL__null;
2736 m_stack_work = NULL__null;
2737 m_stack_rows = NULL__null;
2738 m_stack_values = NULL__null;
2739
2740 // Return
2741 return;
2742}
2743
2744
2745/***********************************************************************//**
2746 * @brief Determines element index for (row,column)
2747 *
2748 * @param[in] row Row index.
2749 * @param[in] column Column index.
2750 *
2751 * Returns the index in the compressed array for (row,col). The following
2752 * special results exist:
2753 * -1: Requested index does not exist in the matrix.
2754 * m_elements: Requested index is the pending element.
2755 ***************************************************************************/
2756int GMatrixSparse::get_index(const int& row, const int& column) const
2757{
2758 // Initialise element to 'not found'
2759 int index = -1;
2760
2761 // If we have a pending element then check if this element is requested
2762 if (m_fill_val != 0.0) {
2763 if (row == m_fill_row && column == m_fill_col) {
2764 return m_elements;
2765 }
2766 }
2767
2768 // If requested element is not the pending element then check if it exists
2769 // in the matrix. Only if it is found its index is returned. Otherwise
2770 // the default index is -1, signalling that the element is absent.
2771 if (m_elements > 0) {
2772 int* ptr_colstart = m_colstart + column;
2773 int i_start = *ptr_colstart++;
2774 int i_stop = *ptr_colstart;
2775 int* ptr_rowinx = m_rowinx + i_start;
2776 for (int i = i_start; i < i_stop; ++i) {
2777 int row_test = *ptr_rowinx++;
2778 if (row_test == row) {
2779 index = i;
2780 break;
2781 }
2782 }
2783 }
2784
2785 // Return index
2786 return index;
2787}
2788
2789
2790/***********************************************************************//**
2791 * @brief Fills pending matrix element
2792 *
2793 * If 'm_fill_val' is non-zero a pending matrix element exists that should
2794 * be filled into (row,col)=(m_fill_row,m_fill_col). This routine performs
2795 * the filling of the matrix with this element and resets 'm_fill_val' to
2796 * zero. This routine allows for element-by-element filling of a sparse
2797 * matrix. This is, of course, very time consuming and should in general
2798 * be avoided. However, it allows to design a sparse matrix class that
2799 * hides the matrix sparsity completely to the user.
2800 ***************************************************************************/
2801void GMatrixSparse::fill_pending(void)
2802{
2803 // If we have a pending element then fill it into the matrix
2804 if (m_fill_val != 0.0) {
2805
2806 // Debugging
2807 #if defined(G_DEBUG_SPARSE_PENDING) || defined(G_DEBUG_SPARSE_INSERTION)
2808 std::cout << "GMatrixSparse::fill_pending(): pending value " <<
2809 m_fill_val << " will be filled in location (" <<
2810 m_fill_row << "," << m_fill_col << ")" << std::endl;
2811 #endif
2812
2813 // If there are so far no elements in the matrix then append element ...
2814 int inx_insert;
2815 if (m_elements == 0) {
2816 inx_insert = 0;
2817 }
2818
2819 // ... otherwise search for index to insert
2820 else {
2821 int* ptr_colstart = m_colstart + m_fill_col;
2822 int i_start = *ptr_colstart++;
2823 int i_stop = *ptr_colstart;
2824 int* ptr_rowinx = m_rowinx + i_start;
2825 for (inx_insert = i_start; inx_insert < i_stop; ++inx_insert) {
2826 int row_test = *ptr_rowinx++;
2827 if (row_test > m_fill_row) {
2828 break;
2829 }
2830 }
2831 }
2832
2833 // Allocate memory for new element
2834 alloc_elements(inx_insert, 1);
2835
2836 // Insert element
2837 m_data[inx_insert] = m_fill_val;
2838 m_rowinx[inx_insert] = m_fill_row;
2839
2840 // Update column start indices
2841 for (int col = m_fill_col+1; col <= m_cols; ++col) {
2842 m_colstart[col] += 1;
2843 }
2844
2845 // Reset fill value
2846 m_fill_val = 0.0;
2847 m_fill_row = 0;
2848 m_fill_col = 0;
2849
2850 // Debugging: show sparse matrix after filling
2851 #if defined(G_DEBUG_SPARSE_PENDING) || defined(G_DEBUG_SPARSE_INSERTION)
2852 std::cout << " Data: ";
2853 for (int i = 0; i < m_elements; ++i) {
2854 std::cout << m_data[i] << " ";
2855 }
2856 std::cout << std::endl << " Row.: ";
2857 for (int i = 0; i < m_elements; ++i) {
2858 std::cout << m_rowinx[i] << " ";
2859 }
2860 std::cout << std::endl << " Col.: ";
2861 for (int i = 0; i < m_cols+1; ++i) {
2862 std::cout << m_colstart[i] << " ";
2863 }
2864 std::cout << std::endl;
2865 #endif
2866
2867 } // endif: a pending matrix element was found
2868
2869 // Return
2870 return;
2871}
2872
2873
2874/***********************************************************************//**
2875 * @brief Allocate memory for new matrix elements
2876 *
2877 * @param[in] start Index of first allocated element.
2878 * @param[in] num Number of elements to be allocated.
2879 *
2880 * Inserts a memory allocation for 'num' elements at the index 'start'.
2881 * The new elements are filled with 0.0 and the corresponding row indices
2882 * are set to 0.
2883 *
2884 * NOTE: This method does not take care of updating 'm_colstart'. This has
2885 * to be done by the client.
2886 ***************************************************************************/
2887void GMatrixSparse::alloc_elements(int start, const int& num)
2888{
2889 // Dump header
2890 #if defined(G_DEBUG_SPARSE_MALLOC)
2891 std::cout << "GMatrixSparse::alloc_elements(start=" << start << ", num=" <<
2892 num << ")" << std::endl;
2893 std::cout << " Before allocation : " << m_elements << " " << m_alloc << std::endl;
2894 #endif
2895
2896 // Continue only if we need memory
2897 if (num > 0) {
2898
2899 // If start is after the end then append memory
2900 if (start > m_elements) {
2901 start = m_elements;
2902 }
2903
2904 // Determine the requested new logical size of the matrix
2905 int new_size = m_elements + num;
2906
2907 // Case A: the requested memory is already available, so just move the
2908 // data to make space for new elements and initialise the new cells
2909 if (new_size <= m_alloc) {
2910
2911 // Move up all elements after index to insert
2912 for (int i = m_elements - 1; i >= start; --i) {
2913 m_data[i+num] = m_data[i];
2914 m_rowinx[i+num] = m_rowinx[i];
2915 }
2916
2917 // Clear new elements (zero content for row 0)
2918 for (int i = start; i < start+num; ++i) {
2919 m_data[i] = 0.0;
2920 m_rowinx[i] = 0;
2921 }
2922
2923 // Update element counter
2924 m_elements += num;
2925
2926 } // endif: Case A: memory already existed
2927
2928 // Case B: more memory is needed, so allocate it, copy the existing
2929 // content, and initialise new cells
2930 else {
2931
2932 // Make sure that enough memory is allocated
2933 int new_propose = m_alloc + m_mem_block;
2934 m_alloc = (new_size > new_propose) ? new_size : new_propose;
2935
2936 // Allocate memory for new elements
2937 double* new_data = new double[m_alloc];
2938 int* new_rowinx = new int[m_alloc];
2939
2940 // Copy all elements before index to insert
2941 for (int i = 0; i < start; ++i) {
2942 new_data[i] = m_data[i];
2943 new_rowinx[i] = m_rowinx[i];
2944 }
2945
2946 // Clear new elements (zero content for row 0)
2947 for (int i = start; i < start+num; ++i) {
2948 new_data[i] = 0.0;
2949 new_rowinx[i] = 0;
2950 }
2951
2952 // Copy all elements after index to insert
2953 for (int i = start; i < m_elements; ++i) {
2954 new_data[i+num] = m_data[i];
2955 new_rowinx[i+num] = m_rowinx[i];
2956 }
2957
2958 // Delete old memory
2959 if (m_data != NULL__null) delete [] m_data;
2960 if (m_rowinx != NULL__null) delete [] m_rowinx;
2961
2962 // Update pointers to new memory and update element counter
2963 m_data = new_data;
2964 m_rowinx = new_rowinx;
2965 m_elements += num;
2966
2967 } // endelse: Case B: more memory was needed
2968
2969 } // endif: needed new memory
2970
2971 // Dump new memory size
2972 #if defined(G_DEBUG_SPARSE_MALLOC)
2973 std::cout << " After allocation .: " << m_elements << " " << m_alloc << std::endl;
2974 #endif
2975
2976 // Return
2977 return;
2978}
2979
2980
2981/***********************************************************************//**
2982 * @brief Free memory for obsolete matrix elements
2983 *
2984 * @param[in] start Index of first freed element.
2985 * @param[in] num Number of elements to be freed.
2986 *
2987 * Free memory for 'num' elements starting from index 'start'.
2988 *
2989 * NOTE: This method does not take care of updating 'm_colstart'. This has
2990 * to be done by the client.
2991 ***************************************************************************/
2992void GMatrixSparse::free_elements(const int& start, const int& num)
2993{
2994 // Dump header
2995 #if defined(G_DEBUG_SPARSE_MALLOC)
2996 std::cout << "GMatrixSparse::free_elements(start=" << start << ", num=" <<
2997 num << ")" << std::endl;
2998 #endif
2999
3000 // Continue only if we need to free memory and if start is within the
3001 // range
3002 if (num > 0 && start < m_elements) {
3003
3004 // Determine the requested new logical size of the matrix
3005 int new_size = m_elements - num;
3006
3007 // If there are no elements then simply delete all matrix elements ...
3008 if (new_size < 1) {
3009 if (m_data != NULL__null) delete [] m_data;
3010 if (m_rowinx != NULL__null) delete [] m_rowinx;
3011 m_data = NULL__null;
3012 m_rowinx = NULL__null;
3013 m_elements = 0;
3014 m_alloc = 0;
3015 }
3016
3017 // ... otherwise shrink the array
3018 else {
3019
3020 // Case A: If at least one entire memory block has been liberated then
3021 // physically shrink the matrix array
3022 if (m_alloc - new_size > m_mem_block) {
3023
3024 // Shrink, but leave a memory block for possible future filling
3025 m_alloc = new_size + m_mem_block;
3026
3027 // Allocate new memory
3028 double* new_data = new double[m_alloc];
3029 int* new_rowinx = new int[m_alloc];
3030
3031 // Copy all elements before the starting index
3032 for (int i = 0; i < start; ++i) {
3033 new_data[i] = m_data[i];
3034 new_rowinx[i] = m_rowinx[i];
3035 }
3036
3037 // Copy all elements after the starting index
3038 for (int i = start; i < new_size; ++i) {
3039 new_data[i] = m_data[i+num];
3040 new_rowinx[i] = m_rowinx[i+num];
3041 }
3042
3043 // Delete old memory
3044 if (m_data != NULL__null) delete [] m_data;
3045 if (m_rowinx != NULL__null) delete [] m_rowinx;
3046
3047 // Update pointers to new memory and update element counter
3048 m_data = new_data;
3049 m_rowinx = new_rowinx;
3050 m_elements = new_size;
3051
3052 } // endif: Case A: memory shrinkage performed
3053
3054 // Case B: we keep the memory and just move the elements
3055 else {
3056
3057 // Move all elements after the starting index
3058 for (int i = start; i < new_size; ++i) {
3059 m_data[i] = m_data[i+num];
3060 m_rowinx[i] = m_rowinx[i+num];
3061 }
3062
3063 // Update element counter
3064 m_elements = new_size;
3065
3066 } // endelse: Case B
3067
3068 } // endif: array shrinkage needed
3069 } // endif: needed new memory
3070
3071 // Return
3072 return;
3073}
3074
3075
3076/***********************************************************************//**
3077 * @brief Remove rows and columns with zeros
3078 *
3079 * @exception GException::matrix_zero
3080 * All matrix elements are zero.
3081 *
3082 * Remove all rows and columns that contain only zeros from matrix. This
3083 * function is needed for compressed matrix factorisation. The resulting
3084 * matrix has reduced size (number of rows and columns) and dimensions
3085 * (number of elements). Note that the physical memory is not reduced by
3086 * the method.
3087 ***************************************************************************/
3088void GMatrixSparse::remove_zero_row_col(void)
3089{
3090 // Dump header
3091 #if defined(G_DEBUG_SPARSE_COMPRESSION)
3092 std::cout << "GMatrixSparse::remove_zero_row_col" << std::endl;
3093 #endif
3094
3095 // Fill pending value in matrix
3096 fill_pending();
3097
3098 // Select non-zero rows and columns of matrix
3099 select_non_zero();
3100
3101 // Stop if there is no compression
3102 if (m_rows == m_num_rowsel && m_cols == m_num_colsel) {
3103 return;
3104 }
3105
3106 // Raise exception if all matrix elements are zero
3107 if (m_num_rowsel < 1 || m_num_colsel < 1) {
3108 throw GException::matrix_zero(G_REMOVE_ZERO"GMatrixSparse::remove_zero_row_col(void)");
3109 }
3110
3111 // Allocate row mapping array
3112 int* row_map = new int[m_rows];
3113
3114 // Setup row mapping array that maps original matrix rows into compressed
3115 // matrix rows. An entry of -1 indicates that the row should be dropped
3116 for (int row = 0; row < m_rows; ++row) {
3117 row_map[row] = -1;
3118 }
3119 for (int c_row = 0; c_row < m_num_rowsel; ++c_row) {
3120 row_map[m_rowsel[c_row]] = c_row;
3121 }
3122
3123 // Initialise pointers to compressed array
3124 double* d_data = m_data;
3125 int* d_rowinx = m_rowinx;
3126
3127 // Initialise column start of first column to zero
3128 m_colstart[0] = 0;
3129
3130 // Dump column start array
3131 #if defined(G_DEBUG_SPARSE_COMPRESSION)
3132 std::cout << " before compression (" << m_colstart[m_cols] << "):";
3133 for (int col = 0; col <= m_cols; ++col)
3134 std::cout << " " << m_colstart[col];
3135 std::cout << std::endl;
3136 #endif
3137
3138 // Loop over all columns of compressed matrix
3139 for (int c_col = 0; c_col < m_num_colsel; ++c_col) {
3140
3141 // Get index range for elements in original matrix
3142 int i_start = m_colstart[m_colsel[c_col]];
3143 int i_stop = m_colstart[m_colsel[c_col]+1];
3144
3145 // Initialise number of element counter for the compressed column
3146 int num = 0;
3147
3148 // Loop over all elements in original column
3149 int c_row;
3150 for (int i = i_start; i < i_stop; ++i) {
3151 if ((c_row = row_map[m_rowinx[i]]) >= 0) {
3152 *d_data++ = m_data[i];
3153 *d_rowinx++ = c_row;
3154 num++;
3155 }
3156 }
3157
3158 // Update column stop
3159 m_colstart[c_col+1] = m_colstart[c_col] + num;
3160
3161 } // endfor: looped over all columns of compressed matrix
3162
3163 // Dump column start array
3164 #if defined(G_DEBUG_SPARSE_COMPRESSION)
3165 std::cout << " after compression (" << m_colstart[m_num_colsel] << ") :";
3166 for (int c_col = 0; c_col <= m_num_colsel; ++c_col)
3167 std::cout << " " << m_colstart[c_col];
3168 std::cout << std::endl;
3169 #endif
3170
3171 // Free row mapping array
3172 delete [] row_map;
3173
3174 // Update matrix size
3175 m_rows = m_num_rowsel;
3176 m_cols = m_num_colsel;
3177
3178 // Return
3179 return;
3180}
3181
3182
3183/***********************************************************************//**
3184 * @brief Insert zero rows and columns
3185 *
3186 * @param[in] rows Number of rows.
3187 * @param[in] cols Number of columns.
3188 *
3189 * Insert zero rows and columns into matrix. Since for a sparse matrix
3190 * this does not require any allocation of additional memory, the data are
3191 * not moved by this function, but the pointers are re-arranged.
3192 ***************************************************************************/
3193void GMatrixSparse::insert_zero_row_col(const int& rows, const int& cols)
3194{
3195 // Dump header
3196 #if defined(G_DEBUG_SPARSE_COMPRESSION)
3197 std::cout << "GMatrixSparse::insert_zero_row_col(" << rows << "," << cols <<
3198 ")" << std::endl;
3199 #endif
3200
3201 // Fill pending value in matrix
3202 fill_pending();
3203
3204 // Stop if there is no insertion
3205 if (m_rows == rows && m_cols == cols) {
3206 return;
3207 }
3208
3209 // If row selection exists then restore row indices
3210 if (m_rowsel != NULL__null) {
3211 for (int i = 0; i < m_elements; ++i) {
3212 m_rowinx[i] = m_rowsel[m_rowinx[i]];
3213 }
3214 }
3215
3216 // Dump column start array
3217 #if defined(G_DEBUG_SPARSE_COMPRESSION)
3218 std::cout << " before restoration (" << m_colstart[m_num_colsel] << "):";
3219 for (int c_col = 0; c_col <= m_num_colsel; ++c_col)
3220 std::cout << " " << m_colstart[c_col];
3221 std::cout << std::endl;
3222 #endif
3223
3224 // If column selection exists then restore column counters
3225 if (m_colsel != NULL__null) {
3226
3227 // Start insertion from the last original column
3228 int col_stop = cols - 1;
3229
3230 // Loop over all compressed columns
3231 for (int c_col = m_num_colsel-1; c_col > 0; --c_col) {
3232 int col_start = m_colsel[c_col-1] + 1;
3233 int col_value = m_colstart[c_col];
3234 for (int col = col_start; col <= col_stop; ++col) {
3235 m_colstart[col] = col_value;
3236 }
3237 col_stop = col_start - 1;
3238 }
3239
3240 // Set first columns if they are not yet set
3241 for (int col = 0; col <= col_stop; ++col) {
3242 m_colstart[col] = 0;
3243 }
3244
3245 // Restore the number of elements
3246 m_colstart[cols] = m_elements;
3247 }
3248
3249 // Dump column start array
3250 #if defined(G_DEBUG_SPARSE_COMPRESSION)
3251 std::cout << " after restoration (" << m_colstart[cols] << ") :";
3252 for (int col = 0; col <= cols; ++col) {
3253 std::cout << " " << m_colstart[col];
3254 }
3255 std::cout << std::endl;
3256 #endif
3257
3258 // Update matrix size
3259 m_rows = rows;
3260 m_cols = cols;
3261
3262 // Return
3263 return;
3264}
3265
3266
3267/***********************************************************************//**
3268 * @brief Prepare mix of sparse columns
3269 *
3270 * @param[in] src1_row Row index array [0...src1_num-1] of first column.
3271 * @param[in] src1_num Number of elements in first column.
3272 * @param[in] src2_row Row index array [0...src2_num-1] of second column.
3273 * @param[in] src2_num Number of elements in second column.
3274 * @param[out] num_1 Number of elements only found in column 1.
3275 * @param[out] num_2 Number of elements only found in column 2.
3276 * @param[out] num_mix Number of elements found in both columns.
3277 *
3278 * This method prepares the mix of two sparse matrix columns into a single
3279 * column.
3280 ***************************************************************************/
3281void GMatrixSparse::mix_column_prepare(const int* src1_row, int src1_num,
3282 const int* src2_row, int src2_num,
3283 int* num_1, int* num_2, int* num_mix)
3284{
3285 // Initialise element counters
3286 *num_1 = 0;
3287 *num_2 = 0;
3288 *num_mix = 0;
3289
3290 // Initialise indices and row indices of both columns
3291 int inx_1 = 0; // Column 1 element index
3292 int inx_2 = 0; // Column 2 element index
3293 int row_1 = src1_row[inx_1++]; // Column 1 first row index
3294 int row_2 = src2_row[inx_2++]; // Column 2 first row index
3295
3296 // Mix elements of both columns while both contain still elements
3297 while (inx_1 < src1_num && inx_2 < src2_num) {
3298
3299 // Case A: the element exist in both columns
3300 if (row_1 == row_2) {
3301 row_1 = src1_row[inx_1++];
3302 row_2 = src2_row[inx_2++];
3303 (*num_mix)++;
3304 }
3305
3306 // Case B: the element exists only in first column
3307 else if (row_1 < row_2) {
3308 row_1 = src1_row[inx_1++];
3309 (*num_1)++;
3310 }
3311
3312 // Case C: the element exists only in second column
3313 else {
3314 row_2 = src2_row[inx_2++];
3315 (*num_2)++;
3316 }
3317
3318 } // endwhile: mixing
3319
3320 // At this point either the first or the second column expired of elements
3321 // In the case that there are still elements remaining in the first column we
3322 // count them now ...
3323 if (inx_1 < src1_num) {
3324 *num_1 += (src1_num - inx_1);
3325 }
3326
3327 // ... or in the case that there are still elements remaining in the second
3328 // column we count them now
3329 if (inx_2 < src2_num) {
3330 *num_2 += (src2_num - inx_2);
3331 }
3332
3333 // We're done
3334 return;
3335}
3336
3337
3338/***********************************************************************//**
3339 * @brief Mix of sparse columns
3340 *
3341 * @param[in] src1_data Data array [0...src1_num-1] of first column.
3342 * @param[in] src1_row Row index array [0...src1_num-1] of first column.
3343 * @param[in] src1_num Number of elements in first column.
3344 * @param[in] src2_data Data array [0...src2_num-1] of second column.
3345 * @param[in] src2_row Row index array [0...src2_num-1] of second column.
3346 * @param[in] src2_num Number of elements in second column.
3347 * @param[in] dst_data Data array [0...dst_num-1] of result column.
3348 * @param[in] dst_row Row index array [0...dst_num-1] of result column.
3349 * @param[in] dst_num Number of elements in result column.
3350 *
3351 * This method mixes two sparse matrix columns into a single column.
3352 ***************************************************************************/
3353void GMatrixSparse::mix_column(const double* src1_data, const int* src1_row,
3354 int src1_num,
3355 const double* src2_data, const int* src2_row,
3356 int src2_num,
3357 double* dst_data, int* dst_row, int* dst_num)
3358{
3359 // Initialise indices and row indices of both columns
3360 int inx_1 = 0; // Column 1 element index
3361 int inx_2 = 0; // Column 2 element index
3362 int inx = 0; // Result column element index
3363 int row_1 = src1_row[inx_1];
3364 int row_2 = src2_row[inx_2];
3365
3366 // Mix elements of both columns while both contain still elements
3367 while (inx_1 < src1_num && inx_2 < src2_num) {
3368
3369 // Case A: the element exists in both columns, so we add up the values
3370 if (row_1 == row_2) {
3371 dst_data[inx] = src1_data[inx_1++] + src2_data[inx_2++];
3372 dst_row[inx] = row_1;
3373 if (inx_1 < src1_num) {
3374 row_1 = src1_row[inx_1];
3375 }
3376 if (inx_2 < src2_num) {
3377 row_2 = src2_row[inx_2];
3378 }
3379 }
3380
3381 // Case B: the element exists only in first column, so we copy the element
3382 // from the first column
3383 else if (row_1 < row_2) {
3384 dst_data[inx] = src1_data[inx_1++];
3385 dst_row[inx] = row_1;
3386 if (inx_1 < src1_num) {
3387 row_1 = src1_row[inx_1];
3388 }
3389 }
3390
3391 // Case C: the element exists only in second column, so we copy the element
3392 // from the second column
3393 else {
3394 dst_data[inx] = src2_data[inx_2++];
3395 dst_row[inx] = row_2;
3396 if (inx_2 < src2_num) {
3397 row_2 = src2_row[inx_2];
3398 }
3399 }
3400
3401 // Update the destination index since we added a element
3402 inx++;
3403
3404 } // endwhile: mixing
3405
3406 // At this point either the first or the second column expired of elements
3407 // In the case that there are still elements remaining in the first column we
3408 // add them now ...
3409 for (int i = inx_1; i < src1_num; ++i, ++inx) {
3410 dst_data[inx] = src1_data[i];
3411 dst_row[inx] = src1_row[i];
3412 }
3413
3414 // ... or in the case that there are still elements remaining in the second
3415 // column we add them now
3416 for (int i = inx_2; i < src2_num; ++i, ++inx) {
3417 dst_data[inx] = src2_data[i];
3418 dst_row[inx] = src2_row[i];
3419 }
3420
3421 // Now store the number of columns in the second column
3422 *dst_num = inx;
3423
3424 // We're done
3425 return;
3426}
3427
3428
3429/*==========================================================================
3430 = =
3431 = Friend functions =
3432 = =
3433 ==========================================================================*/
3434
3435/***********************************************************************//**
3436 * @brief cs_symperm
3437 *
3438 * @param[in] matrix Matrix.
3439 * @param[in] pinv TBD.
3440 *
3441 * Returns matrix(p,p) where matrix and matrix(p,p) are symmetric the upper
3442 * part stored.
3443 ***************************************************************************/
3444GMatrixSparse cs_symperm(const GMatrixSparse& matrix, const int* pinv)
3445{
3446 // Declare loop variables
3447 int i, j, p, q, i2, j2;
3448
3449 // Assign matrix attributes
3450 int n = matrix.m_cols;
3451 int* Ap = matrix.m_colstart;
3452 int* Ai = matrix.m_rowinx;
3453 double* Ax = matrix.m_data;
3454
3455 // Allocate result matrix
3456 GMatrixSparse C(n, n, Ap[n]);
1
Calling constructor for 'GMatrixSparse'
7
Returning from constructor for 'GMatrixSparse'
3457
3458 // Allocate and initialise workspace
3459 int wrk_size = n;
3460 int* wrk_int = new int[wrk_size];
3461 for (i = 0; i < wrk_size; ++i) {
8
Loop condition is true. Entering loop body
9
Loop condition is false. Execution continues on line 3466
3462 wrk_int[i] = 0;
3463 }
3464
3465 // Assign result matrix attributes
3466 int* Cp = C.m_colstart;
3467 int* Ci = C.m_rowinx;
10
'Ci' initialized to a null pointer value
3468 double* Cx = C.m_data;
3469
3470 // Count entries in each column of C
3471 for (j = 0; j < n; j++) {
11
Loop condition is true. Entering loop body
15
Loop condition is false. Execution continues on line 3486
3472
3473 // Column j of A is column j2 of C
3474 j2 = pinv ? pinv[j] : j;
12
Assuming 'pinv' is null
13
'?' condition is false
3475
3476 // Loop over entries in column j
3477 for (p = Ap[j]; p < Ap[j+1]; p++) {
14
Loop condition is false. Execution continues on line 3471
3478 i = Ai [p];
3479 if (i > j) continue; // skip lower triangular part of A
3480 i2 = pinv ? pinv[i] : i; // row i of A is row i2 of C
3481 wrk_int[G_MAX(i2, j2)(((i2) > (j2)) ? (i2) : (j2))]++; // column count of C
3482 }
3483 }
3484
3485 // Compute column pointers of C
3486 cs_cumsum(Cp, wrk_int, n);
3487
3488 // Loop over all columns of A
3489 for (j = 0 ; j < n ; j++) {
16
Loop condition is true. Entering loop body
3490
3491 // Column j of A is column j2 of C
3492 j2 = pinv ? pinv[j] : j;
17
'?' condition is false
3493
3494 // Loop over entries in column j
3495 for (p = Ap[j]; p < Ap[j+1]; p++) {
18
Loop condition is true. Entering loop body
3496 i = Ai [p] ;
3497 if (i > j) continue; // skip lower triangular part of A
19
Assuming 'i' is <= 'j'
20
Taking false branch
3498 i2 = pinv ? pinv[i] : i; // row i of A is row i2 of C
21
'?' condition is false
3499 Ci[q = wrk_int[G_MAX(i2,j2)(((i2) > (j2)) ? (i2) : (j2))]++] = G_MIN(i2,j2)(((i2) < (j2)) ? (i2) : (j2));
22
Array access (from variable 'Ci') results in a null pointer dereference
3500 if (Cx) Cx[q] = Ax[p];
3501 }
3502 }
3503
3504 // Free workspace
3505 delete [] wrk_int;
3506
3507 // Rectify the number of elements in matrix C
3508 C.free_elements(Cp[n], (C.m_elements-Cp[n]));
3509
3510 // Return result
3511 return C;
3512}
3513
3514
3515/***************************************************************************
3516 * @brief Compute transpose matrix
3517 *
3518 * @param[in] matrix Matrix.
3519 * @param[in] values Flag that signals if values should be copied.
3520 *
3521 * The flag 'values' allows to avoid copying the actual data values. This
3522 * allows to perform a logical matrix transposition, as needed by the
3523 * symbolic matrix analysis class.
3524 *
3525 * Note that this method does not support pending elements (they have to be
3526 * filled before).
3527 ***************************************************************************/
3528GMatrixSparse cs_transpose(const GMatrixSparse& matrix, int values)
3529{
3530 // Declare and allocate result matrix
3531 GMatrixSparse result(matrix.m_cols, matrix.m_rows, matrix.m_elements);
3532
3533 // Allocate and initialise workspace
3534 int wrk_size = matrix.m_rows;
3535 int* wrk_int = new int[wrk_size];
3536 for (int i = 0; i < wrk_size; ++i) {
3537 wrk_int[i] = 0;
3538 }
3539
3540 // Setup the number of non-zero elements in each row
3541 // for (p = 0 ; p < Ap [n] ; p++) w [Ai [p]]++ ;
3542 // Ap[n] = m.m_colstart[col]
3543 // n = m.m_cols
3544 // Ai[p] = m.m_rowinx[p]
3545 for (int p = 0; p < matrix.m_colstart[matrix.m_cols]; ++p) {
3546 wrk_int[matrix.m_rowinx[p]]++;
3547 }
3548
3549 // Set row pointers. To use a GSparseSymbolic function we have to
3550 // allocate and object (but this does not take memory)
3551 cs_cumsum(result.m_colstart, wrk_int, matrix.m_rows);
3552
3553 // Case A: Normal transponse, including assignment of values
3554 if (values) {
3555 for (int col = 0; col < matrix.m_cols; ++col) {
3556 for (int p = matrix.m_colstart[col]; p < matrix.m_colstart[col+1] ; ++p) {
3557 int i = wrk_int[matrix.m_rowinx[p]]++;
3558 result.m_rowinx[i] = col;
3559 result.m_data[i] = matrix.m_data[p] ;
3560 }
3561 }
3562 }
3563
3564 // Case B: Logical transponse, no assignment of values is performed
3565 else {
3566 for (int col = 0; col < matrix.m_cols; ++col) {
3567 for (int p = matrix.m_colstart[col]; p < matrix.m_colstart[col+1] ; ++p) {
3568 result.m_rowinx[wrk_int[matrix.m_rowinx[p]]++] = col;
3569 }
3570 }
3571 }
3572
3573 // Free workspace
3574 delete [] wrk_int;
3575
3576 // Return transponse matrix
3577 return result;
3578}
3579
3580
3581/***********************************************************************//**
3582 * @brief cs_cumsum
3583 *
3584 * @param[out] p Integer array (n+1 elements).
3585 * @param[in] c Integer array (n elements).
3586 * @param[in] n Number of elements.
3587 *
3588 * Evaluate p[0..n] = cumulative sum of c[0..n-1]
3589 ***************************************************************************/
3590double cs_cumsum(int* p, int* c, int n)
3591{
3592 // Signal error if one of the input pointers is NULL
3593 if (!p || !c) return (-1);
3594
3595 // Initialise sums (integer and double)
3596 int nz = 0;
3597 double nz2 = 0.0;
3598
3599 // Evaluate p[0..n] = cumulative sum of c[0..n-1]
3600 for (int i = 0; i < n; ++i) {
3601 p[i] = nz ;
3602 nz += c[i];
3603 nz2 += c[i]; // also in double to avoid int overflow
3604 c[i] = p[i]; // also copy p[0..n-1] back into c[0..n-1]
3605 }
3606 p[n] = nz ;
3607
3608 // Return cumulative sum of c[0..n-1]
3609 return nz2;
3610}