GammaLib 2.1.0.dev
Loading...
Searching...
No Matches
GMatrixSparse.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * GMatrixSparse.cpp - Sparse matrix class *
3 * ----------------------------------------------------------------------- *
4 * copyright (C) 2006-2025 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_H
29#include <config.h>
30#endif
31#include <cmath>
32#include "GException.hpp"
33#include "GTools.hpp"
34#include "GVector.hpp"
35#include "GVectorSparse.hpp"
36#include "GMatrix.hpp"
37#include "GMatrixSparse.hpp"
38#include "GMatrixSymmetric.hpp"
39#include "GSparseSymbolic.hpp"
40#include "GSparseNumeric.hpp"
41
42/* __ Method name definitions ____________________________________________ */
43#define G_CONSTRUCTOR "GMatrixSparse::GMatrixSparse(int&, int&, int&)"
44#define G_OP_MUL_VEC "GMatrixSparse::operator*(GVector&)"
45#define G_OP_ADD "GMatrixSparse::operator+=(GMatrixSparse&)"
46#define G_OP_SUB "GMatrixSparse::operator-=(GMatrixSparse&)"
47#define G_OP_MAT_MUL "GMatrixSparse::operator*=(GMatrixSparse&)"
48#define G_AT "GMatrixSparse::at(int&, int&)"
49#define G_EXTRACT_ROW "GMatrixSparse::row(int&)"
50#define G_SET_ROW "GMatrixSparse::row(int&, GVector&)"
51#define G_EXTRACT_COLUMN "GMatrixSparse::column(int&)"
52#define G_SET_COLUMN "GMatrixSparse::column(int&, GVector&)"
53#define G_SET_COLUMN2 "GMatrixSparse::column(int&, double*, int*, int)"
54#define G_ADD_TO_COLUMN1 "GMatrixSparse::add_to_column(int&, GVector&)"
55#define G_ADD_TO_COLUMN2 "GMatrixSparse::add_to_column(int&,"\
56 " const GVectorSparse& vector)"
57#define G_ADD_TO_COLUMN3 "GMatrixSparse::add_to_column(int&,"\
58 " double*, int*, int)"
59#define G_MULTIPLY_COLUMN "GMatrixSparse::multiply_column(int&, GVector&)"
60#define G_CHOL_DECOMP "GMatrixSparse::cholesky_decompose(bool)"
61#define G_CHOL_SOLVE "GMatrixSparse::cholesky_solver(GVector&, bool)"
62#define G_STACK_INIT "GMatrixSparse::stack_init(int&, int&)"
63#define G_STACK_PUSH1 "GMatrixSparse::stack_push_column(GVector&, int&)"
64#define G_STACK_PUSH2 "GMatrixSparse::stack_push_column(double*, int*, int&,"\
65 " int&)"
66#define G_STACK_FLUSH "GMatrixSparse::stack_flush(void)"
67#define G_COLUMN_TO_VECTOR1 "GMatrixSparse::column_to_vector(int&, GVector*)"
68#define G_COLUMN_TO_VECTOR2 "GMatrixSparse::column_to_sparse_vector("\
69 "int&, GVectorSparse*)"
70#define G_COLFILL_SET_COLUMN "GMatrixSparse::colfill_set_column(int&,"\
71 " GVector&)"
72#define G_COLFILL_FLUSH "GMatrixSparse::colfill_flush()"
73#define G_COPY_MEMBERS "GMatrixSparse::copy_members(GMatrixSparse&)"
74#define G_ALLOC_MEMBERS "GMatrixSparse::alloc_members(int&, int&, int&)"
75#define G_GET_INDEX "GMatrixSparse::get_index(int&, int&)"
76#define G_ALLOC "GMatrixSparse::alloc_elements(int&, int&)"
77#define G_FREE "GMatrixSparse::free_elements(int&, int&)"
78#define G_REMOVE_ZERO "GMatrixSparse::remove_zero_row_col(void)"
79#define G_INSERT_ROW "GMatrixSparse::insert_row(int&, GVector&, bool&)"
80#define G_SYMPERM "cs_symperm(GMatrixSparse*, int*, int&)"
81#define G_TRANSPOSE "cs_transpose(GMatrixSparse*, int)"
82
83/* __ Macros _____________________________________________________________ */
84#define G_MIN(a,b) (((a) < (b)) ? (a) : (b))
85#define G_MAX(a,b) (((a) > (b)) ? (a) : (b))
86
87/* __ Coding definitions _________________________________________________ */
88
89/* __ Debug definitions __________________________________________________ */
90//#define G_DEBUG_SPARSE_PENDING // Analyse pending values
91//#define G_DEBUG_SPARSE_INSERTION // Analyse value insertion
92//#define G_DEBUG_SPARSE_ADDITION // Analyse value addition
93//#define G_DEBUG_SPARSE_COMPRESSION // Analyse zero row/column compression
94//#define G_DEBUG_SPARSE_MALLOC // Analyse memory management
95//#define G_DEBUG_SPARSE_STACK_PUSH // Analyse stack column pushing
96//#define G_DEBUG_SPARSE_STACK_FLUSH // Analyse stack column flushing
97
98
99/*==========================================================================
100 = =
101 = Constructors/destructors =
102 = =
103 ==========================================================================*/
104
105/***********************************************************************//**
106 * @brief Void matrix constructor
107 *
108 * Constructs empty sparse matrix. The number of rows and columns of the
109 * matrix will be zero.
110 ***************************************************************************/
112{
113 // Initialise class members
114 init_members();
115
116 // Return
117 return;
118}
119
120
121
122/***********************************************************************//**
123 * @brief Matrix constructor
124 *
125 * @param[in] rows Number of rows [>=0].
126 * @param[in] columns Number of columns [>=0].
127 * @param[in] elements Number of allocated elements.
128 *
129 * @exception GException::invalid_argument
130 * Number of rows or columns is negative.
131 *
132 * Constructs sparse matrix of dimension @p rows times @p columns. The
133 * optional @p elements argument specifies the size of the physical
134 * memory that should be allocated. By default, no memory will be allocated
135 * and memory allocation will be performed on-the-fly. If the amount of
136 * required memory is larger than the size specified by @p elements,
137 * additional momeory will be allocated automatically on-the-fly.
138 ***************************************************************************/
140 const int& columns,
141 const int& elements) :
143{
144 // Compile option: raise an exception if number of rows or columns is
145 // negative
146 #if defined(G_RANGE_CHECK)
147 if (rows < 0) {
148 std::string msg = "Number of rows "+gammalib::str(rows)+" is negative. "
149 "Please specify a non-negative number of rows.";
151 }
152 if (columns < 0) {
153 std::string msg = "Number of columns "+gammalib::str(columns)+" is "
154 "negative. Please specify a non-negative number of "
155 "columns.";
157 }
158 #endif
159
160 // Initialise private members for clean destruction
161 init_members();
162
163 // Allocate matrix memory
164 alloc_members(rows, columns, elements);
165
166 // Return
167 return;
168}
169
170
171/***********************************************************************//**
172 * @brief GMatrix to GMatrixSparse storage class convertor
173 *
174 * @param[in] matrix Generic matrix (GMatrix).
175 *
176 * Constructs a sparse matrix by using the number of rows and columns of
177 * a generic matrix and by assigning the elements of the generic matrix
178 * to the sparse matrix.
179 ***************************************************************************/
181{
182 // Initialise class members for clean destruction
183 init_members();
184
185 // Construct matrix
186 alloc_members(matrix.rows(), matrix.columns());
187
188 // Fill matrix column by column
189 for (int col = 0; col < m_cols; ++col) {
190 GVector vector = matrix.column(col);
191 this->column(col, vector);
192 }
193
194 // Return
195 return;
196}
197
199/***********************************************************************//**
200 * @brief GMatrixSymmetric to GMatrixSparse storage class convertor
201 *
202 * @param[in] matrix Symmetric matrix (GMatrixSymmetric).
203 *
204 * Constructs a sparse matrix by using the number of rows and columns of
205 * a symmetric matrix and by assigning the elements of the symmetric matrix
206 * to the sparse matrix.
207 ***************************************************************************/
209{
210 // Initialise class members for clean destruction
211 init_members();
212
213 // Allocate matrix memory
214 alloc_members(matrix.rows(), matrix.columns());
215
216 // Fill matrix column by column
217 for (int col = 0; col < m_cols; ++col) {
218 GVector vector = matrix.column(col);
219 this->column(col, vector);
220 }
221
222 // Return
223 return;
224}
225
226
227/***********************************************************************//**
228 * @brief Copy constructor
229 *
230 * @param[in] matrix Matrix.
231 *
232 * Constructs matrix by copying an existing matrix.
233 ***************************************************************************/
235{
236 // Initialise private members for clean destruction
237 init_members();
238
239 // Copy members. Note that the copy operation does not fill the
240 // pending element.
241 copy_members(matrix);
242
243 // Return
244 return;
245}
246
247
248/***********************************************************************//**
249 * @brief Destructor
250 ***************************************************************************/
252{
253 // Free members
254 free_members();
255
256 // Return
257 return;
258}
259
260
261/*==========================================================================
262 = =
263 = Operators =
264 = =
265 ==========================================================================*/
266
267/***********************************************************************//**
268 * @brief Matrix assignment operator
269 *
270 * @param[in] matrix Matrix.
271 * @return Matrix.
272 *
273 * Assigns the content of another matrix to the actual matrix instance.
274 ***************************************************************************/
276{
277 // Execute only if object is not identical
278 if (this != &matrix) {
279
280 // Assign base class members. Note that this method will also perform
281 // the allocation of the matrix memory and the copy of the matrix
282 // attributes.
283 this->GMatrixBase::operator=(matrix);
284
285 // Free derived class members
286 free_members();
287
288 // Initialise derived class members
289 init_members();
290
291 // Copy derived class members
292 copy_members(matrix);
293
294 } // endif: object was not identical
295
296 // Return
297 return *this;
298}
299
300
301/***********************************************************************//**
302 * @brief Value assignment operator
303 *
304 * @param[in] value Value.
305 * @return Matrix.
306 *
307 * Assigns the specified @p value to all elements of the matrix.
308 ***************************************************************************/
310{
311 // Continue only if matrix is not empty
312 if (!is_empty()) {
313
314 // Fill any pending element to have a non-pending state
315 fill_pending();
316
317 // If value is 0 then simply reinitialize column start indices
318 if (value == 0) {
319
320 // Initialise column start indices to 0
321 for (int col = 0; col <= m_cols; ++col) {
322 m_colstart[col] = 0;
323 }
324
325 }
326
327 // ... otherwise fill column-wise
328 else {
329
330 // Set column vector
332 column = value;
333
334 // Column-wise setting
335 for (int col = 0; col < m_cols; ++col) {
336 this->column(col, column);
337 }
338
339 }
340
341 } // endif: matrix was not empty
342
343 // Return this object
344 return *this;
345}
346
347
348/***********************************************************************//**
349 * @brief Return reference to matrix element
350 *
351 * @param[in] row Matrix row [0,...,rows()-1].
352 * @param[in] column Matrix column [0,...,columns()-1].
353 * @return Reference to matrix element.
354 *
355 * Returns the reference to the matrix element at @p row and @p column. If
356 * the matrix element does not yet exist, a reference to the pending element
357 * with a value of 0.0 is returned.
358 *
359 * If the matrix row or column are invalid an exception is thrown by the
360 * get_index() method.
361 ***************************************************************************/
362double& GMatrixSparse::operator()(const int& row, const int& column)
363{
364 // Fill pending element. This will set the value of the pending element
365 // to 0.0.
366 fill_pending();
367
368 // Get element
369 int inx = get_index(row, column);
370 double* value;
371 if (inx < 0) {
372 value = &m_fill_val;
373 m_fill_row = row;
375 }
376 else {
377 value = &(m_data[inx]);
378 }
379
380 // Return element
381 return *value;
382}
383
384
385/***********************************************************************//**
386 * @brief Return reference to matrix element (const version)
387 *
388 * @param[in] row Matrix row [0,...,rows()-1].
389 * @param[in] column Matrix column [0,...,columns()-1].
390 * @return Const reference to matrix element.
391 *
392 * Returns a const reference to the matrix element at @p row and @p column.
393 * If the matrix element does not yet exist, a reference to the zero element
394 * is returned. If the matrix element corresponds to the pending element,
395 * a reference to the pending element is returned. Otherwise, a reference
396 * to the matrix elements is returned.
397 *
398 * If the matrix row or column are invalid an exception is thrown by the
399 * get_index() method.
400 ***************************************************************************/
401const double& GMatrixSparse::operator()(const int& row,
402 const int& column) const
403{
404 // Get element. We need here the zero element to return also a pointer
405 // for 0.0 entry that is not stored. Since we have the const version we
406 // don't have to care about modification of this zero value.
407 int inx = get_index(row, column);
408 double* value;
409 if (inx < 0) {
410 value = (double*)&m_zero;
411 }
412 else if (inx == m_elements) {
413 value = (double*)&m_fill_val;
414 }
415 else {
416 value = (double*)&(m_data[inx]);
417 }
418
419 // Return element
420 return *value;
421}
422
423
424/***********************************************************************//**
425 * @brief Vector multiplication
426 *
427 * @param[in] vector Vector.
428 *
429 * @exception GException::invalid_argument
430 * Vector size differs from number of columns in matrix.
431 *
432 * This method performs a vector multiplication of a matrix. The vector
433 * multiplication will produce a vector. The matrix multiplication can only
434 * be performed when the number of matrix columns is equal to the length of
435 * the vector.
436 *
437 * The method includes any pending fill.
438 ***************************************************************************/
440{
441 // Throw exception if the matrix and vector dimensions are not compatible
442 if (m_cols != vector.size()) {
443 std::string msg = "Vector size "+gammalib::str(vector.size())+" does "
444 "not match "+gammalib::str(m_cols)+" matrix columns. "
445 "Please specify a vector of size "+
448 }
449
450 // Initialise result vector
451 GVector result(m_rows);
452
453 // Multiply only if there are elements in matrix
454 if (m_elements > 0) {
455
456 // Perform vector multiplication
457 for (int col = 0; col < m_cols; ++col) {
458 int i_start = m_colstart[col];
459 int i_stop = m_colstart[col+1];
460 double* ptr_data = m_data + i_start;
461 int* ptr_rowinx = m_rowinx + i_start;
462 for (int i = i_start; i < i_stop; ++i) {
463 result[*ptr_rowinx++] += *ptr_data++ * vector[col];
464 }
465 }
466
467 // If fill is pending then add-in also this element into the product
468 if (m_fill_val != 0.0) {
469 result[m_fill_row] += m_fill_val * vector[m_fill_col];
470 #if defined(G_DEBUG_SPARSE_PENDING)
471 std::cout << G_OP_MUL_VEC << ": pending value " << m_fill_val
472 << " for location (" << m_fill_row << "," << m_fill_col
473 << ") has been used." << std::endl;
474 #endif
475 }
476
477 } // endif: there were elements in matrix
478
479 // Return result
480 return result;
481}
482
483
484/***********************************************************************//**
485 * @brief Negate matrix elements
486 *
487 * @return Matrix with negated elements.
488 ***************************************************************************/
490{
491 // Copy matrix
492 GMatrixSparse matrix = *this;
493
494 // Fill pending element
495 matrix.fill_pending();
496
497 // Negate all matrix elements
498 double* ptr = matrix.m_data;
499 for (int i = 0; i < matrix.m_elements; ++i, ++ptr) {
500 *ptr = -(*ptr);
501 }
502
503 // Return matrix
504 return matrix;
505}
506
507
508/***********************************************************************//**
509 * @brief Equalty operator
510 *
511 * @param[in] matrix Matrix.
512 *
513 * This operator checks if two matrices are identical. Two matrices are
514 * considered identical if they have the same dimensions and identicial
515 * elements.
516 *
517 * @todo Implement native sparse code
518 ***************************************************************************/
520{
521 // Initalise the result to 'equal matrices'
522 bool result = true;
523
524 // Perform comparison (only if matrix dimensions are identical)
525 if (m_rows == matrix.m_rows && m_cols == matrix.m_cols) {
526
527 // Loop over all matrix elements
528 for (int row = 0; row < m_rows; ++row) {
529 for (int col = 0; col < m_cols; ++col) {
530 if ((*this)(row,col) != matrix(row,col)) {
531 result = false;
532 break;
533 }
534 }
535 if (!result) {
536 break;
537 }
538 }
539 }
540 else {
541 result = false;
542 }
543
544 // Return result
545 return result;
546}
547
548
549/***********************************************************************//**
550 * @brief Non-equality operator
551 *
552 * @param[in] matrix Matrix.
553 *
554 * This operator checks if two matrices are not identical. Two matrices are
555 * considered not identical if they differ in their dimensions or if at
556 * least one element differs.
557 ***************************************************************************/
559{
560 // Get negated result of equality operation
561 bool result = !(this->operator==(matrix));
562
563 // Return result
564 return result;
565}
566
567
568/***********************************************************************//**
569 * @brief Unary matrix addition operator
570 *
571 * @param[in] matrix Matrix.
572 *
573 * @exception GException::invalid_argument
574 * Incompatible number of matrix rows or columns.
575 *
576 * Add a @p matrix to the current matrix. The operation can only succeed if
577 * both matrices have identical dimensions. If this is not the case an
578 * exception is thrown.
579 ***************************************************************************/
581{
582 // Throw an exception if the matrix dimensions are not compatible
583 if (m_rows != matrix.m_rows) {
584 std::string msg = "Number of matrix rows "+gammalib::str(matrix.m_rows)+
585 " differs from the expected number of "+
586 gammalib::str(m_rows)+" rows. Please specify a "
587 "matrix with "+gammalib::str(m_rows)+" rows.";
589 }
590 if (m_cols != matrix.m_cols) {
591 std::string msg = "Number of matrix columns "+gammalib::str(matrix.m_cols)+
592 " differs from the expected number of "+
593 gammalib::str(m_cols)+" columns. Please specify a "
594 "matrix with "+gammalib::str(m_cols)+" columns.";
596 }
597
598 // Continue only if matrix is not empty
599 if ((m_rows > 0) && (m_cols > 0)) {
600
601 // Prepare for column filling
602 colfill_init();
603
604 // Allocate sparse vectors for matrix addition
605 GVectorSparse v_left(m_rows, m_rows);
606 GVectorSparse v_right(m_rows, m_rows);
607 GVectorSparse v_result(m_rows, m_rows);
608
609 // Perform inplace matrix addition
610 for (int col = 0; col < m_cols; ++col) {
611
612 // Extract sparse left and right vectors
613 column_to_vector(col, &v_left);
614 matrix.column_to_vector(col, &v_right);
615
616 // Initialise sparse vector element indices
617 int i_left = 0;
618 int i_right = 0;
619 int i_result = 0;
620
621 // Flag if elements remain in sparse vectors
622 bool remains_left = (i_left < v_left.elements());
623 bool remains_right = (i_right < v_right.elements());
624
625 // Walk through both vectors as long as some elements remain
626 while (remains_left || remains_right) {
627
628 // Case A: elements remain in both vectors
629 if (remains_left && remains_right) {
630
631 // Get current row indices in both vectors
632 int inx_left = v_left.inx(i_left);
633 int inx_right = v_right.inx(i_right);
634
635 // If both row indices are identical then add-up both
636 // values and move to next elements in both vectors
637 if (inx_left == inx_right) {
638 v_result.m_inx[i_result] = inx_left;
639 v_result.m_data[i_result] = v_left.m_data[i_left] + v_right.m_data[i_right];
640 i_result++;
641 i_left++;
642 i_right++;
643 }
644
645 // ... else if left row index is smaller than right row
646 // index then copy left value and move to next element
647 else if (inx_left < inx_right) {
648 v_result.m_inx[i_result] = inx_left;
649 v_result.m_data[i_result] = v_left.m_data[i_left];
650 i_result++;
651 i_left++;
652 }
653
654 // ... otherwise copy right value and move to next element
655 else {
656 v_result.m_inx[i_result] = inx_right;
657 v_result.m_data[i_result] = v_right.m_data[i_right];
658 i_result++;
659 i_right++;
660 }
661
662 } // endif: elements remained in both vectors
663
664 // Case B: elements remain in left vector
665 else if (remains_left) {
666 v_result.m_inx[i_result] = v_left.inx(i_left);
667 v_result.m_data[i_result] = v_left.m_data[i_left];
668 i_result++;
669 i_left++;
670 }
671
672 // Case C: elements remain in right vector
673 else {
674 v_result.m_inx[i_result] = v_right.inx(i_right);
675 v_result.m_data[i_result] = v_right.m_data[i_right];
676 i_result++;
677 i_right++;
678 }
679
680 // Update remain flags
681 remains_left = (i_left < v_left.elements());
682 remains_right = (i_right < v_right.elements());
683
684 } // endwhile: elements remained in vectors
685
686 // Set number of elements in result vector
687 v_result.m_elements = i_result;
688
689 // Put result vector in matrix
690 colfill_set_column(col, v_result);
691
692 } // endfor: looped over columns
693
694 // Flush columns into matrix
696
697 } // endif: matrices were not empty
698
699 // Return result
700 return *this;
701}
702
703
704/***********************************************************************//**
705 * @brief Unary matrix scalar addition operator
706 *
707 * @param[in] scalar Scalar.
708 *
709 * Adds a @p scalar to each matrix element.
710 ***************************************************************************/
712{
713 // Perform inplace matrix addition using vectors
714 for (int col = 0; col < m_cols; ++col) {
715 GVector vector = column(col);
716 vector += scalar;
717 column(col, vector);
718 }
719
720 // Return result
721 return *this;
722}
723
724
725/***********************************************************************//**
726 * @brief Unary matrix subtraction operator
727 *
728 * @param[in] matrix Matrix.
729 *
730 * @exception GException::invalid_argument
731 * Incompatible matrix size.
732 *
733 * Subtracts a @p matrix from the current matrix. The operation can only
734 * succeed if both matrices have identical dimensions. If this is not the
735 * case an exception is thrown.
736 ***************************************************************************/
738{
739 // Throw an exception if the matrix dimensions are not compatible
740 if (m_rows != matrix.m_rows) {
741 std::string msg = "Number of matrix rows "+gammalib::str(matrix.m_rows)+
742 " differs from the expected number of "+
743 gammalib::str(m_rows)+" rows. Please specify a "
744 "matrix with "+gammalib::str(m_rows)+" rows.";
746 }
747 if (m_cols != matrix.m_cols) {
748 std::string msg = "Number of matrix columns "+gammalib::str(matrix.m_cols)+
749 " differs from the expected number of "+
750 gammalib::str(m_cols)+" columns. Please specify a "
751 "matrix with "+gammalib::str(m_cols)+" columns.";
753 }
754
755 // Continue only if matrix is not empty
756 if ((m_rows > 0) && (m_cols > 0)) {
757
758 // Prepare for column filling
759 colfill_init();
760
761 // Allocate sparse vectors for matrix addition
762 GVectorSparse v_left(m_rows, m_rows);
763 GVectorSparse v_right(m_rows, m_rows);
764 GVectorSparse v_result(m_rows, m_rows);
765
766 // Perform inplace matrix addition
767 for (int col = 0; col < m_cols; ++col) {
768
769 // Extract sparse left and right vectors
770 column_to_vector(col, &v_left);
771 matrix.column_to_vector(col, &v_right);
772
773 // Initialise sparse vector element indices
774 int i_left = 0;
775 int i_right = 0;
776 int i_result = 0;
777
778 // Flag if elements remain in sparse vectors
779 bool remains_left = (i_left < v_left.elements());
780 bool remains_right = (i_right < v_right.elements());
781
782 // Walk through both vectors as long as some elements remain
783 while (remains_left || remains_right) {
784
785 // Case A: elements remain in both vectors
786 if (remains_left && remains_right) {
787
788 // Get current row indices in both vectors
789 int inx_left = v_left.inx(i_left);
790 int inx_right = v_right.inx(i_right);
791
792 // If both row indices are identical then subtract both
793 // values and move to next elements in both vectors
794 if (inx_left == inx_right) {
795 v_result.m_inx[i_result] = inx_left;
796 v_result.m_data[i_result] = v_left.m_data[i_left] - v_right.m_data[i_right];
797 i_result++;
798 i_left++;
799 i_right++;
800 }
801
802 // ... else if left row index is smaller than right row
803 // index then copy left value and move to next element
804 else if (inx_left < inx_right) {
805 v_result.m_inx[i_result] = inx_left;
806 v_result.m_data[i_result] = v_left.m_data[i_left];
807 i_result++;
808 i_left++;
809 }
810
811 // ... otherwise copy negative right value and move to next
812 // element
813 else {
814 v_result.m_inx[i_result] = inx_right;
815 v_result.m_data[i_result] = -v_right.m_data[i_right];
816 i_result++;
817 i_right++;
818 }
819
820 } // endif: elements remained in both vectors
821
822 // Case B: elements remain in left vector
823 else if (remains_left) {
824 v_result.m_inx[i_result] = v_left.inx(i_left);
825 v_result.m_data[i_result] = v_left.m_data[i_left];
826 i_result++;
827 i_left++;
828 }
829
830 // Case C: elements remain in right vector
831 else {
832 v_result.m_inx[i_result] = v_right.inx(i_right);
833 v_result.m_data[i_result] = -v_right.m_data[i_right];
834 i_result++;
835 i_right++;
836 }
837
838 // Update remain flags
839 remains_left = (i_left < v_left.elements());
840 remains_right = (i_right < v_right.elements());
841
842 } // endwhile: elements remained in vectors
843
844 // Set number of elements in result vector
845 v_result.m_elements = i_result;
846
847 // Put result vector in matrix
848 colfill_set_column(col, v_result);
849
850 } // endfor: looped over columns
851
852 // Flush columns into matrix
854
855 } // endif: matrices were not empty
856
857 // Return result
858 return *this;
859}
860
861
862/***********************************************************************//**
863 * @brief Unary matrix scalar subtraction operator
864 *
865 * @param[in] scalar Scalar.
866 *
867 * Subtracts a @p scalar from each matrix element.
868 ***************************************************************************/
870{
871 // Perform inplace matrix subtraction
872 for (int col = 0; col < m_cols; ++col) {
873 GVector vector = column(col);
874 vector -= scalar;
875 column(col, vector);
876 }
877
878 // Return result
879 return *this;
880}
881
882
883/***********************************************************************//**
884 * @brief Unary matrix multiplication operator
885 *
886 * @param[in] matrix Matrix.
887 *
888 * @exception GException::invalid_argument
889 * Number of rows in @p matrix is different from number of
890 * matrix columns.
891 *
892 * This method performs a matrix multiplication. The operation can only
893 * succeed when the dimensions of both matrices are compatible.
894 *
895 * @todo Implement native sparse code
896 ***************************************************************************/
898{
899 // Throw exception if the matrix dimensions are not compatible
900 if (m_cols != matrix.m_rows) {
901 std::string msg = "Number of "+gammalib::str(m_cols)+" columns in "
902 "the first matrix differs from number of "+
903 gammalib::str(matrix.m_rows)+" rows in the second "
904 "matrix. Please specify a second matrix with "+
905 gammalib::str(m_cols)+" rows.";
907 }
908
909 // Allocate result matrix
910 GMatrixSparse result(m_rows, matrix.m_cols);
911
912 // Multiply only if there are elements in both matrices
913 if (m_elements > 0 && matrix.m_elements > 0) {
914
915 // Loop over all elements of result matrix
916 for (int row = 0; row < m_rows; ++row) {
917 for (int col = 0; col < matrix.m_cols; ++col) {
918 double sum = 0.0;
919 for (int i = 0; i < m_cols; ++i) {
920 sum += (*this)(row,i) * matrix(i,col);
921 }
922 result(row,col) = sum;
923 }
924 }
925
926 }
927
928 // Assign result
929 *this = result;
930
931 // Return result
932 return *this;
933}
934
935
936/*==========================================================================
937 = =
938 = Public methods =
939 = =
940 ==========================================================================*/
941
942/***********************************************************************//**
943 * @brief Clear matrix
944 ***************************************************************************/
946{
947 // Free members
948 free_members();
949
950 // Initialise private members
951 init_members();
952
953 // Return
954 return;
955}
956
957
958/***********************************************************************//**
959 * @brief Clone matrix
960 *
961 * @return Pointer to deep copy of matrix.
962 ***************************************************************************/
964{
965 // Clone matrix
966 return new GMatrixSparse(*this);
967}
968
969
970/***********************************************************************//**
971 * @brief Return reference to matrix element
972 *
973 * @param[in] row Matrix row [0,...,rows()[.
974 * @param[in] column Matrix column [0,...,columns()[.
975 * @return Reference to matrix element.
976 *
977 * @exception GException::out_of_range
978 * Matrix row or column out of range.
979 ***************************************************************************/
980double& GMatrixSparse::at(const int& row, const int& column)
981{
982 // Throw exception if row or column index is out of range
984 throw GException::out_of_range(G_AT, "Matrix row", row, m_rows);
985 }
987 throw GException::out_of_range(G_AT, "Matrix column", column, m_cols);
988 }
989
990 // Return element
991 return ((*this)(row, column));
992}
993
994
995/***********************************************************************//**
996 * @brief Return reference to matrix element (const version)
997 *
998 * @param[in] row Matrix row [0,...,rows()[.
999 * @param[in] column Matrix column [0,...,columns()[.
1000 * @return Reference to matrix element.
1001 *
1002 * @exception GException::out_of_range
1003 * Matrix row or column out of range.
1004 ***************************************************************************/
1005const double& GMatrixSparse::at(const int& row, const int& column) const
1006{
1007 // Throw exception if row or column index is out of range
1008 if (row < 0 || row >= m_rows) {
1009 throw GException::out_of_range(G_AT, "Matrix row", row, m_rows);
1010 }
1012 throw GException::out_of_range(G_AT, "Matrix column", column, m_cols);
1013 }
1014
1015 // Return element
1016 return ((*this)(row, column));
1017}
1018
1019
1020/***********************************************************************//**
1021 * @brief Extract row as vector from matrix
1022 *
1023 * @param[in] row Matrix row [0,...,rows()[.
1024 * @return Vector of matrix row elements (columns() elements).
1025 *
1026 * @exception GException::out_of_range
1027 * Invalid matrix row specified.
1028 *
1029 * This method extracts a matrix row into a vector.
1030 ***************************************************************************/
1031GVector GMatrixSparse::row(const int& row) const
1032{
1033 // Throw exception if the row index is invalid
1034 #if defined(G_RANGE_CHECK)
1035 if (row < 0 || row >= m_rows) {
1036 throw GException::out_of_range(G_EXTRACT_ROW, "Matrix row", row, m_rows);
1037 }
1038 #endif
1039
1040 // Create result vector
1041 GVector result(m_cols);
1042
1043 // Loop over all columns to extract data
1044 for (int col = 0; col < m_cols; ++col) {
1045
1046 // Get the start and stop of the elements
1047 int i_start = m_colstart[col];
1048 int i_stop = m_colstart[col+1];
1049
1050 // If column is not empty then search requested row by bisection
1051 if (i_stop > i_start) {
1052
1053 // Find row by bisection
1054 while ((i_stop - i_start) > 1) {
1055 int mid = (i_start+i_stop) / 2;
1056 if (m_rowinx[mid] > row) {
1057 i_stop = mid;
1058 }
1059 else {
1060 i_start = mid;
1061 }
1062 }
1063
1064 // Copy element if we found the row
1065 if (m_rowinx[i_start] == row) {
1066 result[col] = m_data[i_start];
1067 }
1068
1069 } // endif: column was not empty
1070
1071 } // endfor: looped over all columns
1072
1073 // If there is a pending element then put it in the vector
1074 if (m_fill_val != 0.0 && m_fill_row == row) {
1075 result[m_fill_col] = m_fill_val;
1076 }
1077
1078 // Return vector
1079 return result;
1080}
1081
1082
1083/***********************************************************************//**
1084 * @brief Set row in matrix
1085 *
1086 * @param[in] row Matrix row [0,...,rows()[.
1087 * @param[in] vector Vector of matrix row elements (columns() elements).
1088 ***************************************************************************/
1089void GMatrixSparse::row(const int& row, const GVector& vector)
1090{
1091 // Insert row
1092 insert_row(row, vector, false);
1093
1094 // Return
1095 return;
1096}
1097
1098
1099/***********************************************************************//**
1100 * @brief Extract column as vector from matrix
1101 *
1102 * @param[in] column Matrix column [0,...,columns()[.
1103 * @return Vector of matrix column elements (rows() elements).
1104 *
1105 * @exception GException::out_of_range
1106 * Invalid matrix column specified.
1107 *
1108 * This method extracts a matrix column into a vector.
1109 ***************************************************************************/
1110GVector GMatrixSparse::column(const int& column) const
1111{
1112 // Throw exception if the column index is invalid
1113 #if defined(G_RANGE_CHECK)
1115 throw GException::out_of_range(G_EXTRACT_COLUMN, "Matrix column",
1116 column, m_cols);
1117 }
1118 #endif
1119
1120 // Create result vector
1121 GVector result(m_rows);
1122
1123 // Get the start and stop of the elements
1124 int i_start = m_colstart[column];
1125 int i_stop = m_colstart[column+1];
1126
1127 // Extract elements into vector
1128 for (int i = i_start; i < i_stop; ++i) {
1129 result[m_rowinx[i]] = m_data[i];
1130 }
1131
1132 // If there is a pending element then put it in the vector
1133 if (m_fill_val != 0.0 && m_fill_col == column) {
1134 result[m_fill_row] = m_fill_val;
1135 }
1136
1137 // Return vector
1138 return result;
1139}
1140
1141
1142/***********************************************************************//**
1143 * @brief Insert vector column into matrix
1144 *
1145 * @param[in] column Matrix column [0,...,columns()[.
1146 * @param[in] vector Vector.
1147 *
1148 * @exception GException::out_of_range
1149 * Invalid matrix column specified.
1150 * @exception GException::invalid_argument
1151 * Vector size does not match number of matrix rows.
1152 *
1153 * Inserts the content of a vector into a matrix column. Any previous
1154 * content in the matrix column will be overwritted.
1155 *
1156 * This is the main driver routine to insert data into a matrix. Note that
1157 * there is another instance of this function that takes a compressed array.
1158 ***************************************************************************/
1159void GMatrixSparse::column(const int& column, const GVector& vector)
1160{
1161 // Debug header
1162 #if defined(G_DEBUG_SPARSE_INSERTION)
1163 std::cout << "GMatrixSparse::column(";
1164 std::cout << column << ", [" << vector << "]):" << std::endl;
1165 std::cout << " In Data : ";
1166 for (int i = 0; i < m_elements; ++i) {
1167 std::cout << m_data[i] << " ";
1168 }
1169 std::cout << std::endl << " In Row .: ";
1170 for (int i = 0; i < m_elements; ++i) {
1171 std::cout << m_rowinx[i] << " ";
1172 }
1173 std::cout << std::endl << " In Col .: ";
1174 for (int i = 0; i < m_cols+1; ++i) {
1175 std::cout << m_colstart[i] << " ";
1176 }
1177 std::cout << std::endl;
1178 #endif
1179
1180 // Throw exception if the column is invalid
1181 #if defined(G_RANGE_CHECK)
1183 throw GException::out_of_range(G_SET_COLUMN, "Matrix column",
1184 column, m_cols);
1185 }
1186 #endif
1187
1188 // Throw exception if the vector size does not match the number of rows
1189 // in the matrix
1190 if (m_rows != vector.size()) {
1191 std::string msg = "Vector size "+gammalib::str(vector.size())+
1192 " does not match "+gammalib::str(m_rows)+" matrix "
1193 "rows. Please specify a vector of size "+
1194 gammalib::str(m_rows)+".";
1196 }
1197
1198 // If there is a pending element for this column then delete it since
1199 // the vector overwrites this element
1200 if (m_fill_val != 0.0 && m_fill_col == column) {
1201 #if defined(G_DEBUG_SPARSE_PENDING)
1202 std::cout << G_SET_COLUMN << ": pending value " << m_fill_val <<
1203 " for location (" << m_fill_row << "," << m_fill_col <<
1204 ") became obsolete" << std::endl;
1205 #endif
1206 m_fill_val = 0.0;
1207 m_fill_row = 0;
1208 m_fill_col = 0;
1209 }
1210
1211 // Determine the number of non-zero elements in the vector
1212 /*
1213 int n_vector = 0;
1214 for (int row = 0; row < m_rows; ++row) {
1215 if (vector[row] != 0.0) {
1216 n_vector++;
1217 }
1218 }
1219 */
1220 int n_vector = vector.non_zeros();
1221
1222
1223 // Get the start and stop indices of the actual column and compute
1224 // the number of exisiting elements in the column
1225 int i_start = m_colstart[column];
1226 int i_stop = m_colstart[column+1];
1227 int n_exist = i_stop - i_start;
1228
1229 // Compute the size difference for the new matrix. It is positive if
1230 // the number of non-zero entries in the vector is larger than the
1231 // number of non-zero entries in the matrix (in this case we have to
1232 // increase the matrix size).
1233 int n_diff = n_vector - n_exist;
1234
1235 // If we need space then allocate it, if we have too much space then free it
1236 if (n_diff > 0) {
1237 alloc_elements(i_start, n_diff);
1238 #if defined(G_DEBUG_SPARSE_INSERTION)
1239 std::cout << " Insert .: " << n_diff << " elements at index " << i_start << std::endl;
1240 #endif
1241 }
1242 else if (n_diff < 0) {
1243 free_elements(i_start, -n_diff);
1244 #if defined(G_DEBUG_SPARSE_INSERTION)
1245 std::cout << " Remove .: " << -n_diff << " elements at index " << i_start << std::endl;
1246 #endif
1247 }
1248
1249 // Insert the vector elements in the matrix
1250 if (n_vector > 0) {
1251 for (int row = 0, i = i_start; row < m_rows; ++row) {
1252 if (vector[row] != 0.0) {
1253 m_data[i] = vector[row];
1254 m_rowinx[i] = row;
1255 i++;
1256 }
1257 }
1258 }
1259
1260 // Update column start indices
1261 for (int i = column+1; i <= m_cols; ++i) {
1262 m_colstart[i] += n_diff;
1263 }
1264
1265 // Debugging: show sparse matrix after insertion
1266 #if defined(G_DEBUG_SPARSE_INSERTION)
1267 std::cout << " Out Data: ";
1268 for (int i = 0; i < m_elements; ++i) {
1269 std::cout << m_data[i] << " ";
1270 }
1271 std::cout << std::endl << " Out Row : ";
1272 for (int i = 0; i < m_elements; ++i) {
1273 std::cout << m_rowinx[i] << " ";
1274 }
1275 std::cout << std::endl << " Out Col : ";
1276 for (int i = 0; i < m_cols+1; ++i) {
1277 std::cout << m_colstart[i] << " ";
1278 }
1279 std::cout << std::endl;
1280 #endif
1281
1282 // Return
1283 return;
1284}
1285
1286
1287/***********************************************************************//**
1288 * @brief Insert compressed array into matrix column
1289 *
1290 * @param[in] column Matrix column [0,...,columns()[.
1291 * @param[in] values Compressed array.
1292 * @param[in] rows Row numbers of array.
1293 * @param[in] number Number of elements in array.
1294 *
1295 * @exception GException::out_of_range
1296 * Invalid matrix column specified.
1297 * @exception GException::invalid_argument
1298 * Row numbers are not smaller than number of matrix rows
1299 *
1300 * Inserts the content of a copressed array into a matrix column. Any
1301 * previous content in the matrix column will be overwritted.
1302 *
1303 * This is the main driver routine to insert data into a matrix. Note that
1304 * there is another instance of this function that takes a vector.
1305 ***************************************************************************/
1306void GMatrixSparse::column(const int& column, const double* values,
1307 const int* rows, int number)
1308{
1309 // Debug header
1310 #if defined(G_DEBUG_SPARSE_INSERTION)
1311 std::cout << "GMatrixSparse::column(";
1312 std::cout << column << ", values, rows, " << number << "):" << std::endl;
1313 std::cout << " Matrix Data : ";
1314 for (int i = 0; i < m_elements; ++i) {
1315 std::cout << m_data[i] << " ";
1316 }
1317 std::cout << std::endl << " Matrix Row .: ";
1318 for (int i = 0; i < m_elements; ++i) {
1319 std::cout << m_rowinx[i] << " ";
1320 }
1321 std::cout << std::endl << " Matrix Col .: ";
1322 for (int i = 0; i < m_cols+1; ++i) {
1323 std::cout << m_colstart[i] << " ";
1324 }
1325 std::cout << std::endl;
1326 std::cout << " Array Data .: ";
1327 for (int i = 0; i < number; ++i) {
1328 std::cout << values[i] << " ";
1329 }
1330 std::cout << std::endl << " Array Row ..: ";
1331 for (int i = 0; i < number; ++i) {
1332 std::cout << rows[i] << " ";
1333 }
1334 std::cout << std::endl;
1335 #endif
1336
1337 // Throw exception if the column is invalid
1338 #if defined(G_RANGE_CHECK)
1340 throw GException::out_of_range(G_SET_COLUMN2, "Matrix column",
1341 column, m_cols);
1342 }
1343 #endif
1344
1345 // Throw exception if the index array seems incompatible with matrix
1346 // dimensions
1347 if (rows[number-1] >= m_rows) {
1348 std::string msg = "Row number "+gammalib::str(rows[number-1])+" for "
1349 "element "+gammalib::str(number)+" is not smaller "
1350 "than number "+gammalib::str(m_rows)+" of matrix "
1351 "rows. Please specify only row numbers smaller than "+
1352 gammalib::str(m_rows)+".";
1354 }
1355
1356 // If there is a pending element for this column then delete it since
1357 // the vector overwrites this element
1358 if (m_fill_val != 0.0 && m_fill_col == column) {
1359 #if defined(G_DEBUG_SPARSE_PENDING)
1360 std::cout << G_INSERT_COL2 << ": pending value " << m_fill_val <<
1361 " for location (" << m_fill_row << "," << m_fill_col <<
1362 ") became obsolete" << std::endl;
1363 #endif
1364 m_fill_val = 0.0;
1365 m_fill_row = 0;
1366 m_fill_col = 0;
1367 }
1368
1369 // Get the start and stop indices of the actual column and compute
1370 // the number of exisiting elements in the column
1371 int i_start = m_colstart[column];
1372 int i_stop = m_colstart[column+1];
1373 int n_exist = i_stop - i_start;
1374
1375 // If the array is empty then make sure that the number of elements is 0
1376 // (we then just delete the existing column)
1377 if (!values || !rows) {
1378 number = 0;
1379 }
1380
1381 // Compute the size difference for the new matrix. It is positive if
1382 // the number of non-zero entries in the array is larger than the
1383 // number of non-zero entries in the matrix (in this case we have to
1384 // increase the matrix size).
1385 int n_diff = number - n_exist;
1386
1387 // If we need space then allocate it, if we have to much space then free it
1388 if (n_diff > 0) {
1389 alloc_elements(i_start, n_diff);
1390 #if defined(G_DEBUG_SPARSE_INSERTION)
1391 std::cout << " Insert .: " << n_diff << " elements at index " << i_start << std::endl;
1392 #endif
1393 }
1394 else if (n_diff < 0) {
1395 free_elements(i_start, -n_diff);
1396 #if defined(G_DEBUG_SPARSE_INSERTION)
1397 std::cout << " Remove .: " << -n_diff << " elements at index " << i_start << std::endl;
1398 #endif
1399 }
1400
1401 // Insert the array elements into the matrix
1402 if (number > 0) {
1403 for (int row = 0, i = i_start; row < number; ++row, ++i) {
1404 m_data[i] = values[row];
1405 m_rowinx[i] = rows[row];
1406 }
1407 }
1408
1409 // Update column start indices
1410 for (int i = column+1; i <= m_cols; ++i) {
1411 m_colstart[i] += n_diff;
1412 }
1413
1414 // Debugging: show sparse matrix after insertion
1415 #if defined(G_DEBUG_SPARSE_INSERTION)
1416 std::cout << " Out Data: ";
1417 for (int i = 0; i < m_elements; ++i) {
1418 std::cout << m_data[i] << " ";
1419 }
1420 std::cout << std::endl << " Out Row : ";
1421 for (int i = 0; i < m_elements; ++i) {
1422 std::cout << m_rowinx[i] << " ";
1423 }
1424 std::cout << std::endl << " Out Col : ";
1425 for (int i = 0; i < m_cols+1; ++i) {
1426 std::cout << m_colstart[i] << " ";
1427 }
1428 std::cout << std::endl;
1429 #endif
1430
1431 // Return
1432 return;
1433}
1434
1435
1436/***********************************************************************//**
1437 * @brief Add row to matrix elements
1438 *
1439 * @param[in] row Matrix row [0,...,row()[.
1440 * @param[in] vector Vector.
1441 ***************************************************************************/
1442void GMatrixSparse::add_to_row(const int& row, const GVector& vector)
1443{
1444 // Add row
1445 insert_row(row, vector, true);
1446
1447 // Return
1448 return;
1449}
1450
1451
1452/***********************************************************************//**
1453 * @brief Add vector column into matrix
1454 *
1455 * @param[in] column Matrix column [0,...,columns()[.
1456 * @param[in] vector Vector.
1457 *
1458 * @exception GException::out_of_range
1459 * Invalid matrix column specified.
1460 * @exception GException::invalid_argument
1461 * Matrix dimension mismatches the vector size.
1462 *
1463 * Adds the content of a vector to a matrix column.
1464 *
1465 * This is the main driver routine to add data to a matrix. It handles both
1466 * normal and stack-based filled. Note that there is another instance of this
1467 * method that takes a compressed array.
1468 ***************************************************************************/
1469void GMatrixSparse::add_to_column(const int& column, const GVector& vector)
1470{
1471 // Debug header
1472 #if defined(G_DEBUG_SPARSE_ADDITION)
1473 std::cout << "GMatrixSparse::add_col(";
1474 std::cout << column << ", [" << v << "]):" << std::endl;
1475 std::cout << " In Data : ";
1476 for (int i = 0; i < m_elements; ++i) {
1477 std::cout << m_data[i] << " ";
1478 }
1479 std::cout << std::endl << " In Row .: ";
1480 for (int i = 0; i < m_elements; ++i) {
1481 std::cout << m_rowinx[i] << " ";
1482 }
1483 std::cout << std::endl << " In Col .: ";
1484 for (int i = 0; i < m_cols+1; ++i) {
1485 std::cout << m_colstart[i] << " ";
1486 }
1487 std::cout << std::endl;
1488 #endif
1489
1490 // Initialise number of non-zero elements to 0
1491 int non_zero = 0;
1492
1493 // If we have a stack then try to push vector on stack first. Note that
1494 // stack_push_column does its own argument verifications, so to avoid
1495 // double checking we don't do anything before this call ...
1496 if (m_stack_data != NULL) {
1497 non_zero = stack_push_column(vector, column);
1498 if (non_zero == 0) {
1499 return;
1500 }
1501 }
1502
1503 // ... otherwise check first the arguments and determine the number of
1504 // non-zero elements in vector
1505 else {
1506
1507 // Throw exception if the column is invalid
1508 #if defined(G_RANGE_CHECK)
1510 throw GException::out_of_range(G_ADD_TO_COLUMN1, "Matrix column",
1511 column, m_cols);
1512 }
1513 #endif
1514
1515 // Throw exception if the vector size does not match the number of rows
1516 // in the matrix
1517 if (m_rows != vector.size()) {
1518 std::string msg = "Vector size "+gammalib::str(vector.size())+
1519 " does not match "+gammalib::str(m_rows)+" matrix "
1520 "rows. Please specify a vector of size "+
1521 gammalib::str(m_rows)+".";
1523 }
1524
1525 // Determine number of non-zero elements in vector
1526 non_zero = vector.non_zeros();
1527 }
1528
1529 // Extract vector for column, add elements, and re-insert vector (only if
1530 // vector to insert has non-zeros)
1531 if (non_zero > 0) {
1532
1533 // Copy input vector
1534 GVector v_column = vector;
1535
1536 // Add elements to vector
1537 for (int i = m_colstart[column]; i < m_colstart[column+1]; ++i) {
1538 v_column[m_rowinx[i]] += m_data[i];
1539 }
1540
1541 // If there is a pending element then put it in the vector
1542 if (m_fill_val != 0.0 && m_fill_col == column) {
1543 v_column[m_fill_row] += m_fill_val;
1544 }
1545
1546 // Set vector into matrix
1547 this->column(column, v_column);
1548
1549 }
1550
1551 // Debugging: show sparse matrix after addition
1552 #if defined(G_DEBUG_SPARSE_ADDITION)
1553 std::cout << " Out Data: ";
1554 for (int i = 0; i < m_elements; ++i) {
1555 std::cout << m_data[i] << " ";
1556 }
1557 std::cout << std::endl << " Out Row : ";
1558 for (int i = 0; i < m_elements; ++i) {
1559 std::cout << m_rowinx[i] << " ";
1560 }
1561 std::cout << std::endl << " Out Col : ";
1562 for (int i = 0; i < m_cols+1; ++i) {
1563 std::cout << m_colstart[i] << " ";
1564 }
1565 std::cout << std::endl;
1566 #endif
1567
1568 // Return
1569 return;
1570}
1571
1572
1573/***********************************************************************//**
1574 * @brief Add sparse vector into matrix column
1575 *
1576 * @param[in] column Matrix column [0,...,columns()[.
1577 * @param[in] vector Sparse vector.
1578 *
1579 * Adds the content of a sparse vector a matrix column. The method makes
1580 * used of the GMatrixSparse::add_to_column() method for compressed
1581 * arrays, hence for details see below.
1582 ***************************************************************************/
1583void GMatrixSparse::add_to_column(const int& column, const GVectorSparse& vector)
1584{
1585 // Extract arguments from sparse vector
1586 const double* values = vector.m_data;
1587 const int* rows = vector.m_inx;
1588 int number = vector.m_elements;
1589
1590 // Use compressed array method
1591 add_to_column(column, values, rows, number);
1592
1593 // Return
1594 return;
1595}
1596
1597
1598/***********************************************************************//**
1599 * @brief Add compressed array into matrix column
1600 *
1601 * @param[in] column Matrix column [0,...,columns()[.
1602 * @param[in] values Compressed array.
1603 * @param[in] rows Row indices of array.
1604 * @param[in] number Number of elements in array.
1605 *
1606 * @exception GException::out_of_range
1607 * Invalid matrix column specified.
1608 * @exception GException::invalid_argument
1609 * Matrix dimension mismatches the vector size.
1610 *
1611 * Adds the content of a compressed array into a matrix column.
1612 *
1613 * This is the main driver routine to add data to a matrix. It handles both
1614 * normal and stack-based filled. Note that there is another instance of this
1615 * method that takes a vector.
1616 ***************************************************************************/
1617void GMatrixSparse::add_to_column(const int& column, const double* values,
1618 const int* rows, int number)
1619{
1620 // Debug header
1621 #if defined(G_DEBUG_SPARSE_ADDITION)
1622 std::cout << "GMatrixSparse::add_col(";
1623 std::cout << column << ", values, rows, " << number << "):" << std::endl;
1624 std::cout << " Matrix Data : ";
1625 for (int i = 0; i < m_elements; ++i) {
1626 std::cout << m_data[i] << " ";
1627 }
1628 std::cout << std::endl << " Matrix Row .: ";
1629 for (int i = 0; i < m_elements; ++i) {
1630 std::cout << m_rowinx[i] << " ";
1631 }
1632 std::cout << std::endl << " Matrix Col .: ";
1633 for (int i = 0; i < m_cols+1; ++i) {
1634 std::cout << m_colstart[i] << " ";
1635 }
1636 std::cout << std::endl;
1637 std::cout << " Array Data .: ";
1638 for (int i = 0; i < number; ++i) {
1639 std::cout << values[i] << " ";
1640 }
1641 std::cout << std::endl << " Array Row ..: ";
1642 for (int i = 0; i < number; ++i) {
1643 std::cout << rows[i] << " ";
1644 }
1645 std::cout << std::endl;
1646 #endif
1647
1648 // If we have a stack then try to push elements on stack first. Note that
1649 // stack_push_column does its own argument verifications, so to avoid
1650 // double checking we don't do anything before this call ...
1651 if (m_stack_data != NULL) {
1652 number = stack_push_column(values, rows, number, column);
1653 if (number == 0) {
1654 return;
1655 }
1656 }
1657
1658 // ... otherwise check the arguments
1659 else {
1660
1661 // If the array is empty there is nothing to do
1662 if (!values || !rows || (number < 1)) {
1663 return;
1664 }
1665
1666 // Throw exception if the column is invalid
1667 #if defined(G_RANGE_CHECK)
1669 throw GException::out_of_range(G_ADD_TO_COLUMN3, "Matrix column",
1670 column, m_cols);
1671 }
1672 #endif
1673
1674 // Throw exception if the index array seems incompatible with matrix
1675 // dimensions
1676 if (rows[number-1] >= m_rows) {
1677 std::string msg = "Row number "+gammalib::str(rows[number-1])+" for "
1678 "element "+gammalib::str(number)+" is not smaller "
1679 "than number "+gammalib::str(m_rows)+" of matrix "
1680 "rows. Please specify only row numbers smaller than "+
1681 gammalib::str(m_rows)+".";
1683 }
1684
1685 } // endelse: there was no stack
1686
1687 // Get indices of column in matrix
1688 int i_start = m_colstart[column];
1689 int i_stop = m_colstart[column+1];
1690
1691 // Case A: the column exists in the matrix, so mix new elements with existing
1692 // data
1693 if (i_start < i_stop) {
1694
1695 // Fill pending element before the merge (it will be lost otherwise)
1696 fill_pending();
1697
1698 // Allocate workspace to hold combined column
1699 int wrk_size = number + i_stop - i_start;
1700 double* wrk_double = new double[wrk_size];
1701 int* wrk_int = new int[wrk_size];
1702
1703 // Mix matrix column with specified data
1704 int num_mix;
1705 mix_column(&(m_data[i_start]), &(m_rowinx[i_start]), i_stop-i_start,
1706 values, rows, number,
1707 wrk_double, wrk_int, &num_mix);
1708
1709 // Insert mixed column
1710 this->column(column, wrk_double, wrk_int, num_mix);
1711
1712 // Free workspace
1713 delete [] wrk_int;
1714 delete [] wrk_double;
1715
1716 } // endif: Case A
1717
1718 // Case B: the column does not yet exist in the matrix, so just insert it
1719 else {
1720 this->column(column, values, rows, number);
1721 }
1722
1723 // Debugging: show sparse matrix after insertion
1724 #if defined(G_DEBUG_SPARSE_ADDITION)
1725 std::cout << " Out Data: ";
1726 for (int i = 0; i < m_elements; ++i) {
1727 std::cout << m_data[i] << " ";
1728 }
1729 std::cout << std::endl << " Out Row : ";
1730 for (int i = 0; i < m_elements; ++i) {
1731 std::cout << m_rowinx[i] << " ";
1732 }
1733 std::cout << std::endl << " Out Col : ";
1734 for (int i = 0; i < m_cols+1; ++i) {
1735 std::cout << m_colstart[i] << " ";
1736 }
1737 std::cout << std::endl;
1738 #endif
1739
1740 // Return
1741 return;
1742}
1743
1744
1745/***********************************************************************//**
1746 * @brief Multiply matrix column with vector
1747 *
1748 * @param[in] column Matrix column [0,...,columns()[.
1749 * @param[in] vector Vector.
1750 *
1751 * @exception GException::out_of_range
1752 * Invalid matrix column specified.
1753 * @exception GException::invalid_argument
1754 * Matrix dimension mismatches the vector size.
1755 *
1756 * Multiplies the content of a vector with a matrix column.
1757 ***************************************************************************/
1758void GMatrixSparse::multiply_column(const int& column, const GVector& vector)
1759{
1760 // Throw exception if the column is invalid
1761 #if defined(G_RANGE_CHECK)
1763 throw GException::out_of_range(G_MULTIPLY_COLUMN, "Matrix column",
1764 column, m_cols);
1765 }
1766 #endif
1767
1768 // Throw exception if the vector size does not match the number of rows
1769 // in the matrix
1770 if (m_rows != vector.size()) {
1771 std::string msg = "Vector size "+gammalib::str(vector.size())+
1772 " does not match "+gammalib::str(m_rows)+" matrix "
1773 "rows. Please specify a vector of size "+
1774 gammalib::str(m_rows)+".";
1776 }
1777
1778 // Fill pending element into matrix
1779 fill_pending();
1780
1781 // Multiply all non-zero elements of matrix with corresponding
1782 // vector elements
1783 for (int i = m_colstart[column]; i < m_colstart[column+1]; ++i) {
1784 m_data[i] *= vector[m_rowinx[i]];
1785 }
1786
1787 // Return
1788 return;
1789}
1790
1791
1792/***********************************************************************//**
1793 * @brief Return transposed matrix
1794 *
1795 * @return Transposed matrix.
1796 *
1797 * Returns transposed matrix of the matrix.
1798 ***************************************************************************/
1800{
1801 // Copy matrix
1802 GMatrixSparse matrix = *this;
1803
1804 // Fill pending element
1805 matrix.fill_pending();
1806
1807 // Compute the transpose
1808 matrix = cs_transpose(matrix, 1);
1809
1810 // Return matrix
1811 return matrix;
1812}
1813
1814
1815/***********************************************************************//**
1816 * @brief Return inverted matrix
1817 *
1818 * @return Inverted matrix.
1819 *
1820 * Returns inverse of matrix. Inversion is done for the moment using Cholesky
1821 * decomposition. This does not work on any kind of matrix.
1822 *
1823 * @todo Specify in documentation for which kind of matrix the method works.
1824 ***************************************************************************/
1826{
1827 // Copy matrix
1828 GMatrixSparse matrix = *this;
1829
1830 // Fill pending element
1831 matrix.fill_pending();
1832
1833 // Invert matrix
1834 matrix = matrix.cholesky_invert(true);
1835
1836 // Return matrix
1837 return matrix;
1838}
1839
1840
1841/***********************************************************************//**
1842 * @brief Solves linear matrix equation
1843 *
1844 * @param[in] vector Solution vector.
1845 * @return Solutions of matrix equation.
1846 *
1847 * Solves the linear equation
1848 *
1849 * \f[M \times {\tt solution} = {\tt vector} \f]
1850 *
1851 * where \f$M\f$ is the matrix, \f${\tt vector}\f$ is the result, and
1852 * \f${\tt solution}\f$ is the solution. Solving is done using Cholesky
1853 * decomposition. This does not work on any kind of matrix.
1854 *
1855 * If the matrix is empty and the vector has a zero length the method
1856 * returns an empty vector.
1857 *
1858 * @todo Specify in documentation for which kind of matrix the method works.
1859 ***************************************************************************/
1861{
1862 // Initialise result with an empty vector
1863 GVector result;
1864
1865 // Continue only if matrix is not empty or vector size is not zero
1866 if (!is_empty() || vector.size() != 0) {
1867
1868 // Get Cholesky decomposition of matrix
1869 GMatrixSparse decomposition = cholesky_decompose(true);
1870
1871 // Solve linear equation
1872 result = decomposition.cholesky_solver(vector);
1873
1874 }
1875
1876 // Return result
1877 return result;
1878}
1879
1880
1881/***********************************************************************//**
1882 * @brief Return absolute of matrix
1883 *
1884 * @return Absolute of matrix
1885 *
1886 * Returns matrix where all elements of the matrix have been replaced by
1887 * their absolute values.
1888 ***************************************************************************/
1890{
1891 // Copy matrix
1892 GMatrixSparse matrix = *this;
1893
1894 // Fill pending element
1895 matrix.fill_pending();
1896
1897 // Take the absolute value of all matrix elements
1898 double* ptr = matrix.m_data;
1899 for (int i = 0; i < matrix.m_elements; ++i, ++ptr) {
1900 *ptr = std::abs(*ptr);
1901 }
1902
1903 // Return matrix
1904 return matrix;
1905}
1906
1907
1908/***********************************************************************//**
1909 * @brief Returns fill of matrix
1910 *
1911 * The fill of a matrix is defined as the number non-zero elements devided
1912 * by the number of total elements. By definition, the fill is comprised
1913 * in the interval [0,..,1]. The fill of an undefined matrix is defined to
1914 * be 0.
1915 ***************************************************************************/
1916double GMatrixSparse::fill(void) const
1917{
1918 // Initialise result
1919 double result = 0.0;
1920
1921 // Compute matrix size
1922 int size = m_rows*m_cols;
1923
1924 // Continue only if matrix has elements
1925 if (size > 0) {
1926
1927 // Determine number of elements in matrix
1928 int num = (m_fill_val == 0.0) ? m_elements : m_elements + 1;
1929
1930 // Compute fill
1931 result = double(num) / double(size);
1932
1933 } // endif: there were elements in matrix
1934
1935 // Return fill
1936 return result;
1937}
1938
1939
1940/***********************************************************************//**
1941 * @brief Return minimum matrix element
1942 ***************************************************************************/
1943double GMatrixSparse::min(void) const
1944{
1945 // Initialise minimum with fill value
1946 double result = m_fill_val;
1947
1948 // Search all elements for the smallest one
1949 for (int i = 0; i < m_elements; ++i) {
1950 if (m_data[i] < result) {
1951 result = m_data[i];
1952 }
1953 }
1954
1955 // If minimum is > 0.0 and there are zero elements then set the minimum
1956 // to 0.0
1957 if ((result > 0.0) && (m_elements < (m_rows*m_cols))) {
1958 result = 0.0;
1959 }
1960
1961 // Return result
1962 return result;
1963}
1964
1965
1966/***********************************************************************//**
1967 * @brief Return maximum matrix element
1968 ***************************************************************************/
1969double GMatrixSparse::max(void) const
1970{
1971 // Initialise maximum with fill value
1972 double result = m_fill_val;
1973
1974 // Search all elements for the largest one
1975 for (int i = 0; i < m_elements; ++i) {
1976 if (m_data[i] > result) {
1977 result = m_data[i];
1978 }
1979 }
1980
1981 // If maximum is < 0.0 and there are zero elements then set the maximum
1982 // to 0.0
1983 if ((result < 0.0) && (m_elements < (m_rows*m_cols))) {
1984 result = 0.0;
1985 }
1986
1987 // Return result
1988 return result;
1989}
1990
1991
1992/***********************************************************************//**
1993 * @brief Sum matrix elements
1994 ***************************************************************************/
1995double GMatrixSparse::sum(void) const
1996{
1997 // Initialise matrix sum with fill value
1998 double result = m_fill_val;
1999
2000 // Add all elements
2001 for (int i = 0; i < m_elements; ++i) {
2002 result += m_data[i];
2003 }
2004
2005 // Return result
2006 return result;
2007}
2008
2009
2010/***********************************************************************//**
2011 * @brief Return Cholesky decomposition
2012 *
2013 * @param[in] compress Use zero-row/column compression (defaults to true).
2014 * @return Cholesky decomposition of matrix
2015 *
2016 * Returns the Cholesky decomposition of a sparse matrix. The decomposition
2017 * is stored within a GMatrixSparse object.
2018 ***************************************************************************/
2020{
2021 // Create copy of matrix
2022 GMatrixSparse matrix = *this;
2023
2024 // Continue only if matrix is not empty
2025 if (matrix.m_rows > 0 && matrix.m_cols > 0) {
2026
2027 // Save original matrix size
2028 int matrix_rows = matrix.m_rows;
2029 int matrix_cols = matrix.m_cols;
2030
2031 // Delete any existing symbolic and numeric analysis object and reset
2032 // pointers
2033 if (matrix.m_symbolic != NULL) delete matrix.m_symbolic;
2034 if (matrix.m_numeric != NULL) delete matrix.m_numeric;
2035 matrix.m_symbolic = NULL;
2036 matrix.m_numeric = NULL;
2037
2038 // Allocate symbolic analysis object
2039 GSparseSymbolic* symbolic = new GSparseSymbolic();
2040
2041 // Declare numeric analysis object. We don't allocate one since we'll
2042 // throw it away at the end of the function (the L matrix will be copied
2043 // in this object)
2044 GSparseNumeric numeric;
2045
2046 // Fill pending element into matrix
2047 matrix.fill_pending();
2048
2049 // Remove rows and columns containing only zeros if matrix compression
2050 // has been selected
2051 if (compress) {
2052 matrix.remove_zero_row_col();
2053 }
2054
2055 // Ordering an symbolic analysis of matrix. This sets up an array 'pinv'
2056 // which contains the fill-in reducing permutations
2057 symbolic->cholesky_symbolic_analysis(1, matrix);
2058
2059 // Store symbolic pointer in sparse matrix object
2060 matrix.m_symbolic = symbolic;
2061
2062 // Perform numeric Cholesky decomposition
2063 numeric.cholesky_numeric_analysis(matrix, *symbolic);
2064
2065 // Copy L matrix into this object
2066 matrix.free_elements(0, matrix.m_elements);
2067 matrix.alloc_elements(0, numeric.m_L->m_elements);
2068 for (int i = 0; i < matrix.m_elements; ++i) {
2069 matrix.m_data[i] = numeric.m_L->m_data[i];
2070 matrix.m_rowinx[i] = numeric.m_L->m_rowinx[i];
2071 }
2072 for (int col = 0; col <= matrix.m_cols; ++col) {
2073 matrix.m_colstart[col] = numeric.m_L->m_colstart[col];
2074 }
2075
2076 // Insert zero rows and columns if they have been removed previously.
2077 if (compress) {
2078 matrix.insert_zero_row_col(matrix_rows, matrix_cols);
2079 }
2080
2081 } // endif: matrix was not empty
2082
2083 // Return matrix
2084 return matrix;
2085}
2086
2087
2088/***********************************************************************//**
2089 * @brief Cholesky solver
2090 *
2091 * @param[in] vector Solution vector.
2092 * @param[in] compress Request matrix compression.
2093 *
2094 * @exception GException::invalid_argument
2095 * Matrix and vector do not match.
2096 * @exception GException::invalid_value
2097 * Matrix has not been factorised.
2098 *
2099 * Solves the linear equation A*x=b using a Cholesky decomposition of A.
2100 * This function is to be applied on a GMatrixSparse matrix for which a
2101 * Choleksy factorization has been produced using 'cholesky_decompose'.
2102 ***************************************************************************/
2104 const bool& compress) const
2105{
2106 // Dump header
2107 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2108 std::cout << "GMatrixSparse::cholesky_solver" << std::endl;
2109 std::cout << " Input vector .....: " << vector << std::endl;
2110 #endif
2111
2112 // Throw exception if the matrix and vector dimensions are incompatible
2113 if (m_rows != vector.size()) {
2114 std::string msg = "Vector size "+gammalib::str(vector.size())+
2115 " does not match "+gammalib::str(m_rows)+" matrix "
2116 "rows. Please specify a vector of size "+
2117 gammalib::str(m_rows)+".";
2119 }
2120
2121 // Throw exception if there is no symbolic pointer
2122 if (!m_symbolic) {
2123 std::string msg = "Matrix not factorised. Please call method "
2124 "GMatrixSparse::cholesky_decompose() before calling "
2125 "this method.";
2127 }
2128
2129 // Throw exception if there is no permutation
2130 if (!m_symbolic->m_pinv) {
2131 std::string msg = "Matrix not factorised. Please call method "
2132 "GMatrixSparse::cholesky_decompose() before calling "
2133 "this method.";
2135 }
2136
2137 // Flag row and column compression
2138 int row_compressed = (m_rowsel != NULL && m_num_rowsel < m_rows);
2139 int col_compressed = (m_colsel != NULL && m_num_colsel < m_cols);
2140
2141 // Decide if we need a compression algorithm or not
2142 int no_zero = !(compress && (row_compressed || col_compressed));
2143
2144 // Allocate vector for permutation and result vector
2145 GVector result(m_cols);
2146
2147 // Continue only if matrix is not empty
2148 if (m_rows > 0 && m_cols > 0) {
2149
2150 // Setup pointers to L matrix and x vector
2151 int* Lp = m_colstart;
2152 int* Li = m_rowinx;
2153 double* Lx = m_data;
2154
2155 // Case A: no zero-row/col compression needed
2156 if (no_zero) {
2157
2158 // Declare working vector
2159 GVector x(m_rows);
2160
2161 // Perform inverse vector permutation
2162 for (int i = 0; i < vector.size(); ++i) {
2163 x[m_symbolic->m_pinv[i]] = vector[i];
2164 }
2165
2166 // Inplace solve L\x=x
2167 for (int col = 0; col < m_cols; ++col) { // loop over columns
2168 x[col] /= Lx[Lp[col]]; // divide by diag.
2169 for (int p = Lp[col]+1; p < Lp[col+1]; p++) { // loop over elements
2170 x[Li[p]] -= Lx[p] * x[col];
2171 }
2172 }
2173
2174 // Inplace solve L'\x=x
2175 for (int col = m_cols-1; col >= 0; --col) { // loop over columns
2176 for (int p = Lp[col]+1; p < Lp[col+1]; p++) // loop over elements
2177 x[col] -= Lx[p] * x[Li[p]];
2178 x[col] /= Lx[Lp[col]];
2179 }
2180
2181 // Perform vector permutation
2182 for (int i = 0; i < m_cols; ++i) {
2183 result[i] = x[m_symbolic->m_pinv[i]];
2184 }
2185
2186 } // endif: Case A
2187
2188 // Case B: zero-row/column compression requested
2189 else {
2190
2191 // Allocate row and column mapping arrays
2192 int* row_map = new int[m_rows];
2193 int* col_map = new int[m_cols];
2194
2195 // Setup row mapping array that maps original matrix rows into compressed
2196 // matrix rows. An entry of -1 indicates that the row should be dropped.
2197 // If no selection exists then setup an identity map.
2198 if (row_compressed) {
2199 for (int row = 0; row < m_rows; ++row) {
2200 row_map[row] = -1;
2201 }
2202 for (int c_row = 0; c_row < m_num_rowsel; ++c_row) {
2203 row_map[m_rowsel[c_row]] = c_row;
2204 }
2205 }
2206 else {
2207 for (int row = 0; row < m_rows; ++row) {
2208 row_map[row] = row;
2209 }
2210 }
2211 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2212 std::cout << " Row mapping ......:";
2213 for (int row = 0; row < m_rows; ++row) {
2214 std::cout << " " << row_map[row];
2215 }
2216 std::cout << std::endl;
2217 #endif
2218
2219 // Setup column mapping array that maps original matrix column into
2220 // compressed matrix columns. An entry of -1 indicates that the
2221 // column should be dropped. If no selection exists then setup an
2222 // identity map.
2223 if (col_compressed) {
2224 for (int col = 0; col < m_cols; ++col) {
2225 col_map[col] = -1;
2226 }
2227 for (int c_col = 0; c_col < m_num_colsel; ++c_col) {
2228 col_map[m_colsel[c_col]] = c_col;
2229 }
2230 }
2231 else {
2232 for (int col = 0; col < m_cols; ++col) {
2233 col_map[col] = col;
2234 }
2235 }
2236 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2237 std::cout << " Column mapping ...:";
2238 for (int col = 0; col < m_cols; ++col) {
2239 std::cout << " " << col_map[col];
2240 }
2241 std::cout << std::endl;
2242 #endif
2243
2244 // Declare working vector
2245 GVector x(row_compressed ? m_num_rowsel : m_rows);
2246
2247 // Compress input vector v -> c_v if required
2248 if (m_rowsel != NULL && m_num_rowsel < m_rows) {
2249 for (int c_row = 0; c_row < m_num_rowsel; ++c_row) {
2250 x[c_row] = vector[m_rowsel[c_row]];
2251 }
2252 }
2253 else {
2254 x = vector;
2255 }
2256 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2257 std::cout << " Compressed vector : " << x << std::endl;
2258 #endif
2259
2260 // Perform inverse permutation
2261 x = iperm(x, m_symbolic->m_pinv);
2262 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2263 std::cout << " Permutated vector : " << x << std::endl;
2264 #endif
2265
2266 // Inplace solve L\x=x. The column and row maps are just use to see
2267 // which columns or rows should be skipped in the calculations.
2268 for (int col = 0; col < m_cols; ++col) { // loop over columns
2269 int c_col = col_map[col];
2270 if (c_col >= 0) { // use only non-zero cols
2271 x[c_col] /= Lx[Lp[col]]; // divide by diag.
2272 for (int p = Lp[col]+1; p < Lp[col+1]; p++) { // loop over elements
2273 int c_row = row_map[Li[p]];
2274 if (c_row >= 0) { // use only non-zero rows
2275 x[c_row] -= Lx[p] * x[c_col];
2276 }
2277 }
2278 }
2279 }
2280 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2281 std::cout << " Solve Lx=x .......: " << x << std::endl;
2282 #endif
2283
2284 // Inplace solve L'\x=x. The column and row maps are just use to see
2285 // which columns or rows should be skipped in the calculations.
2286 for (int col = m_cols-1; col >= 0; --col) { // loop over columns
2287 int c_col = col_map[col];
2288 if (c_col >= 0) { // use only non-zero cols
2289 for (int p = Lp[col]+1; p < Lp[col+1]; p++) { // loop over elements
2290 int c_row = row_map[Li[p]];
2291 if (c_row >= 0) { // use only non-zero rows
2292 x[c_col] -= Lx[p] * x[c_row];
2293 }
2294 }
2295 x[c_col] /= Lx[Lp[col]];
2296 }
2297 }
2298 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2299 std::cout << " Solve L'x=x ......: " << x << std::endl;
2300 #endif
2301
2302 // Perform vector permutation
2303 x = perm(x, m_symbolic->m_pinv);
2304 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2305 std::cout << " Permutated vector : " << x << std::endl;
2306 #endif
2307
2308 // If column compression has been performed the expand the result
2309 // vector accordingly
2310 if (m_colsel != NULL && m_num_colsel < m_cols) {
2311 for (int c_col = 0; c_col < m_num_colsel; ++c_col) {
2312 result[m_colsel[c_col]] = x[c_col];
2313 }
2314 }
2315 else {
2316 result = x;
2317 }
2318 #if defined(G_DEBUG_SPARSE_COMPRESSION)
2319 std::cout << " Restored vector ..: " << result << std::endl;
2320 #endif
2321
2322 // Free mapping arrays
2323 delete [] row_map;
2324 delete [] col_map;
2325
2326 } // endelse: Case B
2327
2328 } // endif: matrix was not empty
2329
2330 // Return result vector
2331 return result;
2332}
2333
2334
2335/***********************************************************************//**
2336 * @brief Invert matrix using a Cholesky decomposition
2337 *
2338 * @param[in] compress Use zero-row/column compression.
2339 * @return Inverted matrix.
2340 *
2341 * Inverts the matrix using a Cholesky decomposition.
2342 ***************************************************************************/
2344{
2345 // Generate Cholesky decomposition of matrix
2346 GMatrixSparse decomposition = cholesky_decompose(compress);
2347
2348 // Allocate result matrix and unit vector
2349 GMatrixSparse matrix(m_rows, m_cols);
2350 GVector unit(m_rows);
2351
2352 // Column-wise solving of the problem
2353 for (int col = 0; col < m_cols; ++col) {
2354
2355 // Set unit vector
2356 unit[col] = 1.0;
2357
2358 // Solve for column
2359 GVector x = decomposition.cholesky_solver(unit, compress);
2360
2361 // Insert column in matrix
2362 matrix.column(col, x);
2363
2364 // Clear unit vector for next round
2365 unit[col] = 0.0;
2366
2367 }
2368
2369 // Return matrix
2370 return matrix;
2371}
2372
2373
2374/***********************************************************************//**
2375 * @brief Initialises matrix filling stack
2376 *
2377 * @param[in] size Number of matrix elements in stack.
2378 * @param[in] entries Maximum number of columns in stack.
2379 *
2380 * Initialises an internal stack that allows fast column-wise filling or
2381 * the sparse matrix. The stack holds a number of compressed columns, and
2382 * once the stack is full, the stack is flushed by writing the columns in
2383 * the stack into the spare matrix. In that way the memory allocations and
2384 * moves are limited which reduces the CPU cycles needed for filling a
2385 * matrix.
2386 *
2387 * This method needs to be called before the stack can be used. Columns can
2388 * then be filled into the stack using the stack_push_column() methods.
2389 * In case that a column does not fit into the stack a non-zero value is
2390 * returned. The stack is filled in the matrix using the stack_flush()
2391 * method, and the stack can be removed using the stack_destroy() method.
2392 ***************************************************************************/
2393void GMatrixSparse::stack_init(const int& size, const int& entries)
2394{
2395 // Free exisiting stack
2397
2398 // Initialise stack members
2400 m_stack_max_entries = (entries > 0) ? entries : m_cols;
2402
2403 // Allocate stack memory. Raise an exception if allocation fails
2406 m_stack_data = new double[m_stack_size];
2407 m_stack_rowinx = new int[m_stack_size];
2408 m_stack_work = new int[m_cols];
2409 m_stack_rows = new int[m_cols];
2410 m_stack_values = new double[m_cols];
2411
2412 // Initialise next free stack location to the first stack element
2413 m_stack_start[0] = 0;
2414
2415 // Return
2416 return;
2417}
2418
2419
2420/***********************************************************************//**
2421 * @brief Push a vector column on the matrix stack
2422 *
2423 * @param[in] vector Vector.
2424 * @param[in] col Matrix column [0,...,columns()[.
2425 *
2426 * @exception GException::out_of_range
2427 * Invalid matrix column specified.
2428 * @exception GException::invalid_argument
2429 * Matrix dimension mismatches the vector size.
2430 *
2431 * Adds the contect of a vector to the matrix stack. This method is
2432 * identical to the GMatrixSparse::stack_push_column method that uses a
2433 * compressed array, yet it takes a full vector. Internal working arrays
2434 * are used to convert the full column vector in a compressed array and to
2435 * hand it over to the compressed array version.
2436 ***************************************************************************/
2437int GMatrixSparse::stack_push_column(const GVector& vector, const int& col)
2438{
2439 // Raise an exception if the column is invalid
2440 #if defined(G_RANGE_CHECK)
2441 if (col < 0 || col >= m_cols) {
2442 throw GException::out_of_range(G_STACK_PUSH1, "Matrix column",
2443 col, m_cols);
2444 }
2445 #endif
2446
2447 // Raise an exception if the matrix and vector dimensions are incompatible
2448 if (vector.size() != m_rows) {
2449 std::string msg = "Vector size "+gammalib::str(vector.size())+
2450 " does not match "+gammalib::str(m_rows)+" matrix "
2451 "rows. Please specify a vector of size "+
2452 gammalib::str(m_rows)+".";
2454 }
2455
2456 // Initialise number of non-zero elements
2457 int non_zero = 0;
2458
2459 // Compress vector in the buffer and set-up row index array
2460 for (int i = 0; i < vector.size(); ++i) {
2461 if (vector[i] != 0.0) {
2462 m_stack_values[non_zero] = vector[i];
2463 m_stack_rows[non_zero] = i;
2464 non_zero++;
2465 }
2466 }
2467
2468 // Hand over to compressed array method
2469 int remaining = stack_push_column(m_stack_values, m_stack_rows, non_zero, col);
2470
2471 // Return remaining number of elements
2472 return remaining;
2473}
2474
2475
2476/***********************************************************************//**
2477 * @brief Push a compressed array on the matrix stack
2478 *
2479 * @param[in] values Compressed array.
2480 * @param[in] rows Matrix rows of array elements.
2481 * @param[in] number Number of elements in array.
2482 * @param[in] col Matrix column [0,...,columns()[.
2483 * @return Number of elements that have not been put on stack.
2484 *
2485 * @exception GException::out_of_range
2486 * Invalid matrix column specified.
2487 * @exception GException::invalid_argument
2488 * Matrix dimension mismatches the vector size.
2489 *
2490 * The method puts new data on the stack while assuring that each column
2491 * is present only once. If an already existing column is encountered
2492 * the data from the existing and new column are mixed and put into a new
2493 * entry one the stack; the old entry is signalled as obsolete.
2494 *
2495 * On return the method indicates the number of elements in the input
2496 * array that have not been put onto the stack (due to memory limits).
2497 * This value can be either '0' (i.e. all elements have been put on the
2498 * stack) or 'number' (i.e. none of the elements have been put on the
2499 * stack). Columns are not partially put onto the stack.
2500 ***************************************************************************/
2501int GMatrixSparse::stack_push_column(const double* values, const int* rows,
2502 const int& number, const int& col)
2503{
2504 // Raise an exception if the column is invalid
2505 #if defined(G_RANGE_CHECK)
2506 if (col < 0 || col >= m_cols) {
2507 throw GException::out_of_range(G_STACK_PUSH2, "Matrix column",
2508 col, m_cols);
2509 }
2510 #endif
2511
2512 // Initialise return value
2513 int remaining = number;
2514
2515 // Single loop for common exit point
2516 do {
2517
2518 // If the array is empty there is nothing to do
2519 if (!values || !rows || (number < 1)) {
2520 continue;
2521 }
2522
2523 // Raise an exception if the matrix and vector dimensions are
2524 // incompatible. This test needs to be done after the test on a
2525 // positive number!
2526 if (rows[number-1] >= m_rows) {
2527 std::string msg = "Row number "+gammalib::str(rows[number-1])+" for "
2528 "element "+gammalib::str(number)+" is not smaller "
2529 "than number "+gammalib::str(m_rows)+" of matrix "
2530 "rows. Please specify only row numbers smaller than "+
2531 gammalib::str(m_rows)+".";
2533 }
2534
2535 // If there is no stack or the stack can not hold the requested
2536 // number of elements then report number of array elements to the
2537 // caller
2538 if (m_stack_data == NULL || number > m_stack_size) {
2539 continue;
2540 }
2541
2542 // Debug header
2543 #if defined(G_DEBUG_SPARSE_STACK_PUSH)
2544 std::cout << "GMatrixSparse::stack_push_column(v, i, n, col=" << col << ")";
2545 std::cout << std::endl;
2546 std::cout << " Data to push on stack ...:";
2547 for (int i = 0; i < number; ++i) {
2548 std::cout << " " << values[i];
2549 }
2550 std::cout << std::endl;
2551 std::cout << " Row indices of data .....:";
2552 for (int i = 0; i < number; ++i) {
2553 std::cout << " " << rows[i];
2554 }
2555 std::cout << std::endl;
2556 #endif
2557
2558 // If the stack is full then flush it before pushing the array onto it.
2559 // There are 2 conditions that may fill the stack: all entries are
2560 // occupied or there is not enough space to hold more elements
2562 (number >= (m_stack_size - m_stack_start[m_stack_entries]))) {
2563 stack_flush();
2564 }
2565
2566 // If the specified column is already on the stack then mix new column
2567 // with old one and invalidate old column (by setting its column index
2568 // to -1).
2569 // Since we loop here over all stack entries we don't want to have too
2570 // many entries in the stack. So don't set the maximum number of stack
2571 // entries too large ...
2572 for (int entry = 0; entry < m_stack_entries; ++entry) {
2573
2574 // Is the specified column already on the stack?
2575 if (col == m_stack_colinx[entry]) {
2576
2577 // Get start and stop indices of existing column in stack
2578 int i_start = m_stack_start[entry];
2579 int i_stop = m_stack_start[entry+1];
2580
2581 // Allocate variables to hold mixing results
2582 int num_1; // Number of elements that are only in stack column
2583 int num_2; // Number of elements that are only in new column
2584 int num_mix; // Number of elements that are in both columns
2585
2586 // Determine how many elements are requested to hold the combined
2587 // column
2588 mix_column_prepare(&(m_stack_rowinx[i_start]), i_stop-i_start,
2589 rows, number, &num_1, &num_2, &num_mix);
2590 int num_request = num_1 + num_2 + num_mix;
2591
2592 // If there is not enough space in the stack to hold the combined
2593 // column we flush the stack and exit the loop now. In this case
2594 // the new column is put as the first column in the empty (flushed
2595 // stack)
2596 if (num_request >= (m_stack_size - m_stack_start[m_stack_entries])) {
2597 stack_flush();
2598 break;
2599 }
2600
2601 // There is enough space, so combine both columns into a fresh one
2602 int inx = m_stack_start[m_stack_entries];
2603 int num_new;
2604 mix_column(&(m_stack_data[i_start]), &(m_stack_rowinx[i_start]),
2605 i_stop-i_start,
2606 values, rows, number,
2607 &(m_stack_data[inx]), &(m_stack_rowinx[inx]), &num_new);
2608
2609 // Store entry information and initialise start of next entry
2610 m_stack_colinx[m_stack_entries] = col; // Store column index for entry
2611 m_stack_entries++; // Increase the # of entries
2612 m_stack_start[m_stack_entries] = inx + num_new; // Set start pointer for next entry
2613
2614 // Invalidate old column
2615 m_stack_colinx[entry] = -1;
2616
2617 // Fall through to end
2618 remaining = 0;
2619 break;
2620
2621 } // endif: we found an existing column
2622 } // endfor: looped over all columns
2623
2624 // If column has already been inserted then fall through ...
2625 if (remaining == 0) {
2626 continue;
2627 }
2628
2629 // Otherwise, push the array on the stack
2630 int inx = m_stack_start[m_stack_entries];
2631 for (int i = 0; i < number; ++i) {
2632 m_stack_data[inx] = values[i];
2633 m_stack_rowinx[inx] = rows[i];
2634 inx++;
2635 }
2636
2637 // Store entry information and initialise start of next entry
2638 m_stack_colinx[m_stack_entries] = col; // Store column index for entry
2639 m_stack_entries++; // Increase the # of entries
2640 m_stack_start[m_stack_entries] = inx; // Set start pointer for next entry
2641
2642 // Signal success
2643 remaining = 0;
2644
2645 } while (0); // End of main do-loop
2646
2647 // Debug: show stack information
2648 #if defined(G_DEBUG_SPARSE_STACK_PUSH)
2649 std::cout << " Number of stack entries .: " << m_stack_entries << std::endl;
2650 std::cout << " Stack entry columns .....:";
2651 for (int i = 0; i < m_stack_entries; ++i) {
2652 std::cout << " " << m_stack_colinx[i];
2653 }
2654 std::cout << std::endl;
2655 std::cout << " Stack entry starts ......:";
2656 for (int i = 0; i < m_stack_entries; ++i) {
2657 std::cout << " " << m_stack_start[i];
2658 }
2659 std::cout << " (next at " << m_stack_start[m_stack_entries] << ")" << std::endl;
2660 std::cout << " Stack data ..............:";
2661 for (int i = 0; i < m_stack_start[m_stack_entries]; ++i) {
2662 std::cout << " " << m_stack_data[i];
2663 }
2664 std::cout << std::endl;
2665 std::cout << " Stack rows ..............:";
2666 for (int i = 0; i < m_stack_start[m_stack_entries]; ++i) {
2667 std::cout << " " << m_stack_rowinx[i];
2668 }
2669 std::cout << std::endl;
2670 #endif
2671
2672 // Return remaining number of elements
2673 return remaining;
2674}
2675
2676
2677/***********************************************************************//**
2678 * @brief Flush matrix stack
2679 *
2680 * Adds the content of the stack to the actual matrix. First, the total
2681 * number of matrix elements is determined. Then new memory is allocated to
2682 * hold all elements. Finally, all elements are filled into the new memory
2683 * that is then replacing the old matrix memory.
2684 *
2685 * The method uses the internal working array m_stack_work.
2686 *
2687 * NOTE: The present algorithm assumes that each column occurs only once
2688 * in the stack!
2689 ***************************************************************************/
2691{
2692 // Do nothing if there is no stack, no stack entries or the matrix is
2693 // empty
2694 if (m_stack_data == NULL || m_stack_entries == 0 || is_empty()) {
2695 return;
2696 }
2697
2698 // Fill pending value
2699 fill_pending();
2700
2701 // Debug header
2702 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2703 std::cout << "GMatrixSparse::stack_flush" << std::endl;
2704 std::cout << " Number of columns on stack : " << m_stack_entries << std::endl;
2705 std::cout << " Number of elements on stack: " << m_stack_start[m_stack_entries] << std::endl;
2706 std::cout << " Number of matrix elements .: " << m_elements << std::endl;
2707 std::cout << " Col.start at end of matrix : " << m_colstart[m_cols] << std::endl;
2708 #endif
2709
2710 // Use stack working array to flag all columns that exist already in the
2711 // matrix by 1 and all non-existing columns by 0
2712 for (int col = 0; col < m_cols; ++col) {
2713 if (m_colstart[col] < m_colstart[col+1]) {
2714 m_stack_work[col] = 1;
2715 }
2716 else {
2717 m_stack_work[col] = 0;
2718 }
2719 }
2720
2721 // Initialise some element counters
2722 int new_elements = 0; // Number of new elements to add to matrix
2723 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2724 int num_columns = 0; // Number of valid columns in stack
2725 int num_matrix = 0; // Number of elements only in matrix
2726 int num_stack = 0; // Number of elements only on stack
2727 int num_both = 0; // Number of elements in matrix and on stack
2728 #endif
2729
2730 // Loop over all stack entries and gather the number of elements that are
2731 // new with respect to the initial matrix. For each column set a flag with
2732 // the following meanings:
2733 // -(entry+2): Column exists and is also found in stack=entry
2734 // +(entry+2): Column exists in stack=entry only
2735 // 1: Column exists in matrix only
2736 // 0: Column does neither exist in matrix or stack
2737 for (int entry = 0; entry < m_stack_entries; ++entry) {
2738
2739 // Get column for actual entry
2740 int col = m_stack_colinx[entry];
2741
2742 // Consider only valid entries
2743 if (col >= 0) {
2744
2745 // Debug option: update number of valid columns
2746 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2747 num_columns++;
2748 #endif
2749
2750 // If column exists already in matrix then determine total number
2751 // of additional elements
2752 if (m_stack_work[col] == 1) {
2753
2754 // Flag that this column is a mixed column
2755 m_stack_work[col] = -(entry+2);
2756
2757 // Setup index boundaries
2758 int i_start = m_colstart[col];
2759 int i_stop = m_colstart[col+1];
2760 int k_start = m_stack_start[entry];
2761 int k_stop = m_stack_start[entry+1];
2762
2763 // Allocate output counters
2764 int num_1; // Number of elements only found in matrix column
2765 int num_2; // Number of elements only found in stack column
2766 int num_mix; // Number of elements found in both columns
2767
2768 // Analyse column mixing
2769 mix_column_prepare(&(m_rowinx[i_start]), i_stop-i_start,
2770 &(m_stack_rowinx[k_start]), k_stop-k_start,
2771 &num_1, &num_2, &num_mix);
2772
2773 // Update element counters
2774 new_elements += num_2;
2775 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2776 num_matrix += num_1;
2777 num_stack += num_2;
2778 num_both += num_mix;
2779 #endif
2780
2781 } // endif: column existed in the matrix
2782
2783 // If column did not exist in the matrix then consider all
2784 // elements as new
2785 else {
2786 m_stack_work[col] = (entry+2);
2787 new_elements += (m_stack_start[entry+1] - m_stack_start[entry]);
2788 }
2789
2790 } // endif: entry was valid
2791
2792 } // endfor: looped over all entries
2793
2794 // Debug option: Log number of valid columns and elements in new matrix
2795 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2796 std::cout << " Valid columns on stack ....: " << num_columns << std::endl;
2797 std::cout << " Valid elements on stack ...: " << new_elements << std::endl;
2798 #endif
2799
2800 // Compute total number of matrix elements
2801 int elements = m_elements + new_elements;
2802
2803 // Allocate memory for new matrix (always keep some elbow room)
2804 m_alloc = elements + m_mem_block;
2805 double* new_data = new double[m_alloc];
2806 int* new_rowinx = new int[m_alloc];
2807
2808 // Fill new matrix. For this purpose we loop over all matrix columns
2809 // and perform the operation that was identified in the previous scan
2810 int index = 0;
2811 for (int col = 0; col < m_cols; ++col) {
2812
2813 // If column does not exist then skip. Note that the index is stored
2814 // in [col] instead of [col+1] to not disturb the existing matrix.
2815 // The column start indices are corrected later.
2816 if (m_stack_work[col] == 0) {
2817 m_colstart[col] = index;
2818 continue;
2819 }
2820
2821 // If column exists in the matrix but not in the stack we copy the
2822 // existing column into the new matrix
2823 if (m_stack_work[col] == 1) {
2824 for (int i = m_colstart[col]; i < m_colstart[col+1]; ++i) {
2825 new_data[index] = m_data[i];
2826 new_rowinx[index] = m_rowinx[i];
2827 index++;
2828 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2829 num_matrix++;
2830 #endif
2831 }
2832 }
2833
2834 // If column is new (i.e. it did not exist in the matrix before)
2835 // we copy the stack into the new matrix
2836 else if (m_stack_work[col] > 1) {
2837 int entry = m_stack_work[col] - 2;
2838 for (int i = m_stack_start[entry]; i < m_stack_start[entry+1]; ++i) {
2839 new_data[index] = m_stack_data[i];
2840 new_rowinx[index] = m_stack_rowinx[i];
2841 index++;
2842 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2843 num_stack++;
2844 #endif
2845 }
2846 }
2847
2848 // If column exists in the matrix and in the stack then we have to
2849 // mix both
2850 else {
2851
2852 // Get stack entry for column mix
2853 int entry = -(m_stack_work[col] + 2);
2854
2855 // Setup index boundaries
2856 int i_start = m_colstart[col];
2857 int i_stop = m_colstart[col+1];
2858 int k_start = m_stack_start[entry];
2859 int k_stop = m_stack_start[entry+1];
2860
2861 // Allocate output counter
2862 int num;
2863
2864 // Perform column mixing
2865 mix_column(&(m_data[i_start]), &(m_rowinx[i_start]), i_stop-i_start,
2866 &(m_stack_data[k_start]), &(m_stack_rowinx[k_start]), k_stop-k_start,
2867 &(new_data[index]), &(new_rowinx[index]), &num);
2868
2869 // Increment element index
2870 index += num;
2871
2872 } // endelse: column mixing required
2873
2874 // Store actual index in column start array. Note that the index is
2875 // stored in [col] instead of [col+1] to not disturb the existing
2876 // matrix. The column start indices are corrected later.
2877 m_colstart[col] = index;
2878
2879 } // endfor: looped over all columns
2880
2881 // Dump number of elements in new matrix after stack flushing
2882 #if defined(G_DEBUG_SPARSE_STACK_FLUSH)
2883 std::cout << " Added elements ............: " << index;
2884 std::cout << " (should be " << elements << ")" << std::endl;
2885 std::cout << " - Matrix only .............: " << num_matrix << std::endl;
2886 std::cout << " - Stack only ..............: " << num_stack << std::endl;
2887 std::cout << " - Matrix & Stack ..........: " << num_both << std::endl;
2888 #endif
2889
2890 // Correct columns start array
2891 for (int col = m_cols; col > 0; --col) {
2892 m_colstart[col] = m_colstart[col-1];
2893 }
2894 m_colstart[0] = 0;
2895
2896 // Delete old matrix memory
2897 if (m_data != NULL) delete [] m_data;
2898 if (m_rowinx != NULL) delete [] m_rowinx;
2899
2900 // Update pointers to new memory and update element counter
2901 m_data = new_data;
2902 m_rowinx = new_rowinx;
2903 m_elements = elements;
2904
2905 // Stack is empty now, so reset stack counters
2906 m_stack_entries = 0;
2907 m_stack_start[0] = 0;
2908
2909 // Return
2910 return;
2911}
2912
2913
2914/***********************************************************************//**
2915 * @brief Destroy matrix stack
2916 *
2917 * Flush and destroy matrix stack
2918 ***************************************************************************/
2920{
2921 // Flush stack first
2922 stack_flush();
2923
2924 // Free stack members
2926
2927 // Initialise stack (no entries)
2929
2930 // Return
2931 return;
2932}
2933
2934
2935/***********************************************************************//**
2936 * @brief Extract matrix into vector
2937 *
2938 * @param[in] column Matrix column [0,...,columns()[.
2939 * @param[out] vector Pointer to vector.
2940 *
2941 * @exception GException::out_of_range
2942 * Invalid matrix column specified.
2943 * @exception GException::invalid_argument
2944 * No valid @p vector parameter provided.
2945 * @p vector is incompatible with matrix dimensions.
2946 *
2947 * Extracts a matrix column into a preexisting vector. All vector elements
2948 * will overwritten by the method, and in particular zero matrix values will
2949 * be set to zero.
2950 ***************************************************************************/
2951void GMatrixSparse::column_to_vector(const int& column, GVector* vector) const
2952{
2953 // Throw exception if the column index is invalid
2954 #if defined(G_RANGE_CHECK)
2956 throw GException::out_of_range(G_COLUMN_TO_VECTOR1, "Matrix column",
2957 column, m_cols);
2958 }
2959 #endif
2960
2961 // Throw exception if vector is NULL
2962 if (vector == NULL) {
2963 std::string msg = "NULL vector argument provided. Please specify a "
2964 "valid GVector object.";
2966 }
2967
2968 // Throw exception if vector length differs from number of rows
2969 if (vector->size() != m_rows) {
2970 std::string msg = "Vector length "+gammalib::str(vector->size())+
2971 " differs from number of matrix rows "+
2972 gammalib::str(m_rows)+". Please specify a vector "
2973 "with length identical to the number of matrix "
2974 "rows.";
2976 }
2977
2978 // Initialise all vector values to zero
2979 for (int i = 0; i < m_rows; ++i) {
2980 (*vector)[i] = 0.0;
2981 }
2982
2983 // Get the start and stop of the elements
2984 int i_start = m_colstart[column];
2985 int i_stop = m_colstart[column+1];
2986
2987 // Extract matrix elements into vector
2988 for (int i = i_start; i < i_stop; ++i) {
2989 (*vector)[m_rowinx[i]] = m_data[i];
2990 }
2991
2992 // If there is a pending element then put it in the vector
2993 if (m_fill_val != 0.0 && m_fill_col == column) {
2994 (*vector)[m_fill_row] = m_fill_val;
2995 }
2996
2997 // Return
2998 return;
2999}
3000
3001
3002/***********************************************************************//**
3003 * @brief Extract matrix into sparse vector
3004 *
3005 * @param[in] column Matrix column [0,...,columns()[.
3006 * @param[out] vector Pointer to sparse vector.
3007 *
3008 * @exception GException::out_of_range
3009 * Invalid matrix column specified.
3010 * @exception GException::invalid_argument
3011 * No valid @p vector parameter provided.
3012 *
3013 * Extracts a matrix column into a preexisting sparse vector. All sparse
3014 * vector elements will overwritten by the method. The method also sets all
3015 * vector attributes, so consistency between the matrix and the sparse vector
3016 * is enforced. If needed, fresh sparse vector memory is allocated.
3017 ***************************************************************************/
3018void GMatrixSparse::column_to_vector(const int& column, GVectorSparse* vector) const
3019{
3020 // Throw exception if the column index is invalid
3021 #if defined(G_RANGE_CHECK)
3023 throw GException::out_of_range(G_COLUMN_TO_VECTOR2, "Matrix column",
3024 column, m_cols);
3025 }
3026 #endif
3027
3028 // Throw exception if vector is NULL
3029 if (vector == NULL) {
3030 std::string msg = "NULL sparse vector argument provided. Please "
3031 "specify a valid GVectorSparse object.";
3033 }
3034
3035 // Get the start and stop of the elements
3036 int i_start = m_colstart[column];
3037 int i_stop = m_colstart[column+1];
3038
3039 // Initialise sparse vector
3040 vector->m_num = m_rows;
3041 vector->m_elements = i_stop - i_start;
3042 vector->m_colinx = column;
3043
3044 // If there are elements then append them
3045 if (vector->m_elements > 0) {
3046
3047 // Allocate elements if required
3048 if (vector->m_elements > vector->m_alloc) {
3049 vector->alloc_members(vector->m_elements);
3050 }
3051
3052 // Extract matrix elements into sparse vector
3053 for (int i = i_start, idst = 0; i < i_stop; ++i, ++idst) {
3054 vector->m_inx[idst] = m_rowinx[i];
3055 vector->m_data[idst] = m_data[i];
3056 }
3057
3058 }
3059
3060 // If there is a pending element then put it in the vector
3061 if (m_fill_val != 0.0 && m_fill_col == column) {
3062 (*vector)[m_fill_row] = m_fill_val;
3063 }
3064
3065 // Return
3066 return;
3067}
3068
3069
3070/***********************************************************************//**
3071 * @brief Initialise memory for column filling
3072 *
3073 * Allocates memory for column filling. If previous
3074 ***************************************************************************/
3076{
3077 // Free existing column filling low-level members
3079
3080 // Continue only if there are columns
3081 if (m_cols > 0) {
3082
3083 // Allocate pointers to hold columns and row indices
3084 m_columns_length = new int[m_cols];
3085 m_columns_rowinx = new int*[m_cols];
3086 m_columns_data = new double*[m_cols];
3087
3088 // Initialise all pointers to NULL
3089 for (int i = 0; i < m_cols; ++i) {
3090 m_columns_length[i] = 0;
3091 m_columns_rowinx[i] = NULL;
3092 m_columns_data[i] = NULL;
3093 }
3094
3095 } // endif: there were columns
3096
3097 // Return
3098 return;
3099}
3100
3101
3102/***********************************************************************//**
3103 * @brief Set column for column filling
3104 *
3105 * @param[in] column Matrix column [0,...,columns()[.
3106 * @param[in] vector Vector.
3107 *
3108 * @exception GException::invalid_value
3109 * No memory was allocated for quick column filling.
3110 *
3111 * @exception GException::out_of_range
3112 * Invalid matrix column specified.
3113 * @exception GException::invalid_argument
3114 * Vector size does not match number of matrix rows.
3115 *
3116 * Inserts the content of a vector into the column filling memory.
3117 * Any previous content for the selected column will be overwritten.
3118 *
3119 * Note that this method does not fill the actually matrix, but some buffer
3120 * memory that efficiently stores the information. To fill these buffers
3121 * into the matrix, and to actually use the values, call the
3122 * GMatrixSparse::colfill_flush() method.
3123 ***************************************************************************/
3124void GMatrixSparse::colfill_set_column(const int& column, const GVector& vector)
3125{
3126 // Throw an exception if no column filling memory exists
3127 if ((m_columns_rowinx == NULL) || (m_columns_data == NULL)) {
3128 std::string msg = "No memory for column filling was allocated. "
3129 "Please call colfill_init() method before invoking "
3130 "this method.";
3132 }
3133
3134 // Throw exception if the column index is invalid
3135 #if defined(G_RANGE_CHECK)
3137 throw GException::out_of_range(G_COLFILL_SET_COLUMN, "Matrix column",
3138 column, m_cols);
3139 }
3140 #endif
3141
3142 // Throw an exception if the vector size does not match the number of rows
3143 // in the matrix
3144 if (m_rows != vector.size()) {
3145 std::string msg = "Vector size "+gammalib::str(vector.size())+
3146 " does not match "+gammalib::str(m_rows)+" matrix "
3147 "rows. Please specify a vector of size "+
3148 gammalib::str(m_rows)+".";
3150 }
3151
3152 // Determine the number of non-zero elements in the vector
3153 int n_vector = vector.non_zeros();
3154
3155 // Free any existing column memory
3156 if (m_columns_rowinx[column] != NULL) delete [] m_columns_rowinx[column];
3157 if (m_columns_data[column] != NULL) delete [] m_columns_data[column];
3158
3159 // Mark existing memory as free
3160 m_columns_rowinx[column] = NULL;
3161 m_columns_data[column] = NULL;
3162
3163 // If there are non-zero elements then store the values
3164 if (n_vector > 0) {
3165
3166 // Store length of column
3167 m_columns_length[column] = n_vector;
3168
3169 // Allocate memory for column
3170 int* rowinx = new int[n_vector];
3171 double* data = new double[n_vector];
3172
3173 // Store pointers
3174 m_columns_rowinx[column] = rowinx;
3175 m_columns_data[column] = data;
3176
3177 // Fill vector and row indices into memory
3178 for (int row = 0; row < m_rows; ++row) {
3179 if (vector[row] != 0.0) {
3180 *data++ = vector[row];
3181 *rowinx++ = row;
3182 }
3183 }
3184
3185 } // endif: there were non-zero element
3186
3187 // Return
3188 return;
3189}
3190
3191
3192/***********************************************************************//**
3193 * @brief Set column for column filling
3194 *
3195 * @param[in] column Matrix column [0,...,columns()[.
3196 * @param[in] vector Sparse vector.
3197 *
3198 * @exception GException::invalid_value
3199 * No memory was allocated for quick column filling.
3200 * @exception GException::out_of_range
3201 * Invalid matrix column specified.
3202 * @exception GException::invalid_argument
3203 * Vector size does not match number of matrix rows.
3204 *
3205 * Inserts the content of a vector into the column filling memory.
3206 * Any previous content for the selected column will be overwritten.
3207 *
3208 * Note that this method does not fill the actually matrix, but some buffer
3209 * memory that efficiently stores the information. To fill these buffers
3210 * into the matrix, and to actually use the values, call the
3211 * GMatrixSparse::colfill_flush() method.
3212 ***************************************************************************/
3213void GMatrixSparse::colfill_set_column(const int& column, const GVectorSparse& vector)
3214{
3215 // Throw an exception if no quick column filling memory exists
3216 if ((m_columns_rowinx == NULL) || (m_columns_data == NULL)) {
3217 std::string msg = "No memory for column filling was allocated. "
3218 "Please call colfill_init() method before invoking "
3219 "this method.";
3221 }
3222
3223 // Throw exception if the column index is invalid
3224 #if defined(G_RANGE_CHECK)
3226 throw GException::out_of_range(G_COLFILL_SET_COLUMN, "Matrix column",
3227 column, m_cols);
3228 }
3229 #endif
3230
3231 // Throw an exception if the vector size does not match the number of rows
3232 // in the matrix
3233 if (m_rows != vector.size()) {
3234 std::string msg = "Vector size "+gammalib::str(vector.size())+
3235 " does not match "+gammalib::str(m_rows)+" matrix "
3236 "rows. Please specify a vector of size "+
3237 gammalib::str(m_rows)+".";
3239 }
3240
3241 // Free any existing column memory
3242 if (m_columns_rowinx[column] != NULL) delete [] m_columns_rowinx[column];
3243 if (m_columns_data[column] != NULL) delete [] m_columns_data[column];
3244
3245 // Mark existing memory as free
3246 m_columns_rowinx[column] = NULL;
3247 m_columns_data[column] = NULL;
3248
3249 // If there are non-zero elements then store the values
3250 if (vector.elements() > 0) {
3251
3252 // Store length of column
3253 m_columns_length[column] = vector.elements();
3254
3255 // Allocate memory for column
3256 int* rowinx = new int[vector.elements()];
3257 double* data = new double[vector.elements()];
3258
3259 // Store pointers
3260 m_columns_rowinx[column] = rowinx;
3261 m_columns_data[column] = data;
3262
3263 // Fill vector and row indices into memory
3264 for (int i = 0; i < vector.elements(); ++i) {
3265 *data++ = vector.m_data[i];
3266 *rowinx++ = vector.m_inx[i];
3267 }
3268
3269 } // endif: there were non-zero element
3270
3271 // Return
3272 return;
3273}
3274
3275
3276/***********************************************************************//**
3277 * @brief Flush column filling memory into sparse matrix
3278 *
3279 * @exception GException::invalid_value
3280 * No memory was allocated for quick column filling.
3281 *
3282 * Flushes the column filling memory into the space matrix. After flushing,
3283 * the memory for column filling will be free'd.
3284 ***************************************************************************/
3286{
3287 // Throw an exception if no column filling memory exists
3288 if ((m_columns_rowinx == NULL) || (m_columns_data == NULL)) {
3289 std::string msg = "No memory for column filling was allocated. "
3290 "Please call colfill_init() method before invoking "
3291 "this method.";
3293 }
3294
3295 // Reset fill value
3296 m_fill_val = 0.0;
3297 m_fill_row = 0;
3298 m_fill_col = 0;
3299
3300 // Free existing data and row indices
3301 if (m_data != NULL) delete [] m_data;
3302 if (m_rowinx != NULL) delete [] m_rowinx;
3303 m_data = NULL;
3304 m_rowinx = NULL;
3305
3306 // Compute total nomber of elements
3307 m_elements = 0;
3308 for (int i = 0; i < m_cols; ++i) {
3310 }
3311
3312 // Set number of memory size to allocate
3314
3315 // Allocate sparse matrix for all elements
3316 if (m_alloc > 0) {
3317 m_data = new double[m_alloc];
3318 m_rowinx = new int[m_alloc];
3319 }
3320
3321 // Initialise index
3322 int index = 0;
3323
3324 // Fill matrix by looping over all matrix columns
3325 for (int col = 0; col < m_cols; ++col) {
3326
3327 // Set column start index
3328 m_colstart[col] = index;
3329
3330 // Get column pointers
3331 double* data = m_columns_data[col];
3332 int* rowinx = m_columns_rowinx[col];
3333
3334 // If column does not exist then move to next column
3335 if ((m_columns_length[col] == 0) || (data == NULL) || (rowinx == NULL)) {
3336 continue;
3337 }
3338
3339 // ... otherwise fill column data into sparse matrix
3340 else {
3341 for (int i = 0; i < m_columns_length[col]; ++i, ++index) {
3342 m_data[index] = *data++;
3343 m_rowinx[index] = *rowinx++;
3344 }
3345 }
3346
3347 } // endfor: looped over all columns
3348
3349 // Set final column start index
3350 m_colstart[m_cols] = index;
3351
3352 // Free existing quick column filling low-level members
3354
3355 // Return
3356 return;
3357}
3358
3359
3360/***********************************************************************//**
3361 * @brief Print matrix
3362 *
3363 * @param[in] chatter Chattiness.
3364 * @return String containing matrix information
3365 ***************************************************************************/
3366std::string GMatrixSparse::print(const GChatter& chatter) const
3367{
3368 // Initialise result string
3369 std::string result;
3370
3371 // Continue only if chatter is not silent
3372 if (chatter != SILENT) {
3373
3374 // Determine number of elements
3375 int nonzero = 0;
3376 int elements = m_elements;
3377 if (m_cols > 0) {
3378 if (m_fill_val == 0.0) {
3379 nonzero = (m_colstart != NULL) ? m_colstart[m_cols] : 0;
3380 }
3381 else {
3382 nonzero = (m_colstart != NULL) ? m_colstart[m_cols]+1 : 0;
3383 elements++;
3384 }
3385 }
3386
3387 // Append header
3388 result.append("=== GMatrixSparse ===");
3389
3390 // Append information
3391 result.append("\n"+gammalib::parformat("Number of rows"));
3392 result.append(gammalib::str(m_rows));
3393 if (m_rowsel != NULL) {
3394 result.append(" (compressed "+gammalib::str(m_num_rowsel)+")");
3395 }
3396 result.append("\n"+gammalib::parformat("Number of columns"));
3397 result.append(gammalib::str(m_cols));
3398 if (m_colsel != NULL) {
3399 result.append(" (compressed "+gammalib::str(m_num_colsel)+")");
3400 }
3401 result.append("\n"+gammalib::parformat("Number of nonzero elements"));
3402 result.append(gammalib::str(nonzero));
3403 if (m_fill_val != 0.0) {
3404 result.append("\n"+gammalib::parformat("Pending element"));
3405 result.append("("+gammalib::str(m_fill_row)+
3406 ","+gammalib::str(m_fill_col)+")=");
3407 result.append(gammalib::str(m_fill_val));
3408 }
3409 result.append("\n"+gammalib::parformat("Number of allocated cells"));
3410 result.append(gammalib::str(m_alloc));
3411 result.append("\n"+gammalib::parformat("Memory block size"));
3412 result.append(gammalib::str(m_mem_block));
3413 result.append("\n"+gammalib::parformat("Sparse matrix fill"));
3414 result.append(gammalib::str(fill()));
3415 result.append("\n"+gammalib::parformat("Pending element"));
3416 result.append(gammalib::str(m_fill_val));
3417 result.append("\n"+gammalib::parformat("Fill stack size"));
3418 result.append(gammalib::str(m_stack_size));
3419 if (m_stack_data == NULL) {
3420 result.append(" (none)");
3421 }
3422
3423 // Append elements and compression schemes
3424 result.append(print_elements(chatter));
3425 result.append(print_row_compression(chatter));
3426 result.append(print_col_compression(chatter));
3427
3428 } // endif: chatter was not silent
3429
3430 // Return result
3431 return result;
3432}
3433
3434
3435/*==========================================================================
3436 = =
3437 = Private methods =
3438 = =
3439 ==========================================================================*/
3440
3441/***********************************************************************//**
3442 * @brief Initialise class mambers
3443 ***************************************************************************/
3445{
3446 // Initialise sparse matrix members
3447 m_rowinx = NULL;
3449 m_zero = 0.0;
3450 m_fill_val = 0.0;
3451 m_fill_row = 0;
3452 m_fill_col = 0;
3453 m_symbolic = NULL;
3454 m_numeric = NULL;
3455
3456 // Initialise stack members
3458
3459 // Initialise quick column filling low-level members
3460 m_columns_length = NULL;
3461 m_columns_rowinx = NULL;
3462 m_columns_data = NULL;
3463
3464 // Return
3465 return;
3466}
3467
3468
3469/***********************************************************************//**
3470 * @brief Copy class members
3471 *
3472 * @param[in] matrix Matrix to be copied.
3473 ***************************************************************************/
3475{
3476 // Copy GMatrixSparse members
3477 m_mem_block = matrix.m_mem_block;
3478 m_zero = matrix.m_zero;
3479 m_fill_val = matrix.m_fill_val;
3480 m_fill_row = matrix.m_fill_row;
3481 m_fill_col = matrix.m_fill_col;
3482
3483 // Copy row indices if they exist
3484 if (matrix.m_rowinx != NULL && matrix.m_alloc > 0) {
3485 m_rowinx = new int[matrix.m_alloc];
3486 for (int i = 0; i < matrix.m_elements; ++i) {
3487 m_rowinx[i] = matrix.m_rowinx[i];
3488 }
3489 }
3490
3491 // Clone symbolic decomposition if it exists
3492 if (matrix.m_symbolic != NULL) {
3494 *m_symbolic = *matrix.m_symbolic;
3495 }
3496
3497 // Copy numeric decomposition if it exists
3498 if (matrix.m_numeric != NULL) {
3499 m_numeric = new GSparseNumeric();
3500 *m_numeric = *matrix.m_numeric;
3501 }
3502
3503 // Initialise quick column filling low-level members
3504 m_columns_length = NULL;
3505 m_columns_rowinx = NULL;
3506 m_columns_data = NULL;
3507
3508 // Return
3509 return;
3510}
3511
3512
3513/***********************************************************************//**
3514 * @brief Delete class members
3515 ***************************************************************************/
3517{
3518 // De-allocate only if memory has indeed been allocated
3519 if (m_numeric != NULL) delete m_numeric;
3520 if (m_symbolic != NULL) delete m_symbolic;
3521 if (m_rowinx != NULL) delete [] m_rowinx;
3522
3523 // Properly mark members as free
3524 m_rowinx = NULL;
3525 m_symbolic = NULL;
3526 m_numeric = NULL;
3527
3528 // Free stack members
3530
3531 // Free quick column filling low-level members
3533
3534 // Return
3535 return;
3536}
3537
3538
3539/***********************************************************************//**
3540 * @brief Delete quick column filling class members
3541 ***************************************************************************/
3543{
3544 // Free vector of column lengths
3545 if (m_columns_length != NULL) {
3546 delete [] m_columns_length;
3547 m_columns_length = NULL;
3548 }
3549
3550 // Free row indices
3551 if (m_columns_rowinx != NULL) {
3552 for (int i = 0; i < m_cols; ++i) {
3553 if (m_columns_rowinx[i] != NULL) delete [] m_columns_rowinx[i];
3554 }
3555 delete [] m_columns_rowinx;
3556 m_columns_rowinx = NULL;
3557 }
3558
3559 // Free data
3560 if (m_columns_data != NULL) {
3561 for (int i = 0; i < m_cols; ++i) {
3562 if (m_columns_data[i] != NULL) delete [] m_columns_data[i];
3563 }
3564 delete [] m_columns_data;
3565 m_columns_data = NULL;
3566 }
3567
3568 // Return
3569 return;
3570}
3571
3572
3573/***********************************************************************//**
3574 * @brief Allocate matrix
3575 *
3576 * @param[in] rows Number of rows.
3577 * @param[in] columns Number of columns.
3578 * @param[in] elements Number of matrix elements to be physically allocated.
3579 *
3580 * This is the main constructor code that allocates and initialises memory
3581 * for matrix elements. The method only allocates elements if both @p rows
3582 * and @p columns are positive. Otherwise the method does nothing and will
3583 * set the m_rows and m_cols attributes to zero.
3584 ***************************************************************************/
3585void GMatrixSparse::alloc_members(const int& rows, const int& columns,
3586 const int& elements)
3587{
3588 // Continue only if rows and columns are valid
3589 if (rows > 0 && columns > 0) {
3590
3591 // Free any existing memory
3592 if (m_colstart != NULL) delete [] m_colstart;
3593
3594 // Allocate column start array. This is the only array that we can
3595 // allocate at this time. The other arrays can only be allocated
3596 // during filling of the matrix
3597 m_colstart = new int[columns+1];
3598
3599 // Store (logical) matrix size
3600 m_rows = rows;
3601 m_cols = columns;
3602
3603 // Initialise column start indices to 0
3604 for (int col = 0; col <= m_cols; ++col) {
3605 m_colstart[col] = 0;
3606 }
3607
3608 // Optionally allocate memory for matrix elements
3609 if (elements > 0) {
3610 alloc_elements(0, elements);
3611 }
3612
3613 } // endif: number of elements was positive
3614
3615 // Return
3616 return;
3617}
3618
3619
3620/***********************************************************************//**
3621 * @brief Initialise fill stack
3622 *
3623 * The fill stack is used to fill the sparse matrix without any prior know-
3624 * ledge about the number of location of the non-zero matrix elements.
3625 * Values are first filled into the stack and flushed into the sparse
3626 * matrix once the stack is full.
3627 ***************************************************************************/
3629{
3630 // Initialise stack members
3632 m_stack_size = 0;
3633 m_stack_entries = 0;
3634 m_stack_colinx = NULL;
3635 m_stack_start = NULL;
3636 m_stack_data = NULL;
3637 m_stack_rowinx = NULL;
3638 m_stack_work = NULL;
3639 m_stack_rows = NULL;
3640 m_stack_values = NULL;
3641
3642 // Return
3643 return;
3644}
3645
3646
3647/***********************************************************************//**
3648 * @brief Delete fill-stack class members
3649 *
3650 * Deletes all memory that has been allocated using the init_stack_members()
3651 * method.
3652 ***************************************************************************/
3654{
3655 // Free stack members
3656 if (m_stack_colinx != NULL) delete [] m_stack_colinx;
3657 if (m_stack_start != NULL) delete [] m_stack_start;
3658 if (m_stack_data != NULL) delete [] m_stack_data;
3659 if (m_stack_rowinx != NULL) delete [] m_stack_rowinx;
3660 if (m_stack_work != NULL) delete [] m_stack_work;
3661 if (m_stack_rows != NULL) delete [] m_stack_rows;
3662 if (m_stack_values != NULL) delete [] m_stack_values;
3663
3664 // Properly mark members as free
3665 m_stack_colinx = NULL;
3666 m_stack_start = NULL;
3667 m_stack_data = NULL;
3668 m_stack_rowinx = NULL;
3669 m_stack_work = NULL;
3670 m_stack_rows = NULL;
3671 m_stack_values = NULL;
3672
3673 // Return
3674 return;
3675}
3676
3677
3678/***********************************************************************//**
3679 * @brief Determines element index for (row,column)
3680 *
3681 * @param[in] row Matrix row [0,...,m_rows[.
3682 * @param[in] column Matrix column [0,...,m_cols[.
3683 * @return Element index.
3684 *
3685 * @exception GException::out_of_range
3686 * Matrix row or column out of range
3687 *
3688 * Returns the index in the compressed array for (row,col). The following
3689 * special results exist:
3690 *
3691 * -1: Requested index does not exist in the matrix.
3692 * m_elements: Requested index is the pending element.
3693 ***************************************************************************/
3694int GMatrixSparse::get_index(const int& row, const int& column) const
3695{
3696 // Raise an exception if the row or column index is invalid
3697 #if defined(G_RANGE_CHECK)
3698 if (row < 0 || row >= m_rows) {
3699 throw GException::out_of_range(G_GET_INDEX, "Matrix row", row, m_rows);
3700 }
3702 throw GException::out_of_range(G_GET_INDEX, "Matrix column", column, m_cols);
3703 }
3704 #endif
3705
3706 // Initialise element to 'not found'
3707 int index = -1;
3708
3709 // If we have a pending element and the pending element is requested
3710 // then return m_elements as index
3711 if ((m_fill_val != 0.0) && (row == m_fill_row && column == m_fill_col)) {
3712 index = m_elements;
3713 }
3714
3715 // ... otherwise if there are elements in the matrix then search for
3716 // the requested row and column.
3717 else if (m_elements > 0) {
3718
3719 // Get the start and stop of the elements
3720 int* ptr_colstart = m_colstart + column;
3721 int i_start = *ptr_colstart++;
3722 int i_stop = *ptr_colstart;
3723
3724 // If column is not empty then search requested row by bisection
3725 if (i_stop > i_start) {
3726
3727 // Find row by bisection
3728 while ((i_stop - i_start) > 1) {
3729 int mid = (i_start+i_stop) / 2;
3730 if (m_rowinx[mid] > row) {
3731 i_stop = mid;
3732 }
3733 else {
3734 i_start = mid;
3735 }
3736 }
3737
3738 // If we found the row then store the index
3739 if (m_rowinx[i_start] == row) {
3740 index = i_start;
3741 }
3742
3743 } // endif: column was not empty
3744
3745 } // endif: matrix contained elements
3746
3747 // Return index
3748 return index;
3749}
3750
3751
3752/***********************************************************************//**
3753 * @brief Fills pending matrix element
3754 *
3755 * If 'm_fill_val' is non-zero a pending matrix element exists that should
3756 * be filled into (row,col)=(m_fill_row,m_fill_col). This routine performs
3757 * the filling of the matrix with this element and resets 'm_fill_val' to
3758 * zero. This routine allows for element-by-element filling of a sparse
3759 * matrix. This is, of course, very time consuming and should in general
3760 * be avoided. However, it allows to design a sparse matrix class that
3761 * hides the matrix sparsity completely to the user.
3762 ***************************************************************************/
3764{
3765 // If we have a pending element then fill it into the matrix
3766 if (m_fill_val != 0.0) {
3767
3768 // Debugging
3769 #if defined(G_DEBUG_SPARSE_PENDING) || defined(G_DEBUG_SPARSE_INSERTION)
3770 std::cout << "GMatrixSparse::fill_pending(): pending value " <<
3771 m_fill_val << " will be filled in location (" <<
3772 m_fill_row << "," << m_fill_col << ")" << std::endl;
3773 #endif
3774
3775 // If there are so far no elements in the matrix then append element ...
3776 int inx_insert;
3777 if (m_elements == 0) {
3778 inx_insert = 0;
3779 }
3780
3781 // ... otherwise search for index to insert
3782 else {
3783 int* ptr_colstart = m_colstart + m_fill_col;
3784 int i_start = *ptr_colstart++;
3785 int i_stop = *ptr_colstart;
3786 int* ptr_rowinx = m_rowinx + i_start;
3787 for (inx_insert = i_start; inx_insert < i_stop; ++inx_insert) {
3788 int row_test = *ptr_rowinx++;
3789 if (row_test > m_fill_row) {
3790 break;
3791 }
3792 }
3793 }
3794
3795 // Allocate memory for new element
3796 alloc_elements(inx_insert, 1);
3797
3798 // Insert element
3799 m_data[inx_insert] = m_fill_val;
3800 m_rowinx[inx_insert] = m_fill_row;
3801
3802 // Update column start indices
3803 for (int col = m_fill_col+1; col <= m_cols; ++col) {
3804 m_colstart[col] += 1;
3805 }
3806
3807 // Reset fill value
3808 m_fill_val = 0.0;
3809 m_fill_row = 0;
3810 m_fill_col = 0;
3811
3812 // Debugging: show sparse matrix after filling
3813 #if defined(G_DEBUG_SPARSE_PENDING) || defined(G_DEBUG_SPARSE_INSERTION)
3814 std::cout << " Data: ";
3815 for (int i = 0; i < m_elements; ++i) {
3816 std::cout << m_data[i] << " ";
3817 }
3818 std::cout << std::endl << " Row.: ";
3819 for (int i = 0; i < m_elements; ++i) {
3820 std::cout << m_rowinx[i] << " ";
3821 }
3822 std::cout << std::endl << " Col.: ";
3823 for (int i = 0; i < m_cols+1; ++i) {
3824 std::cout << m_colstart[i] << " ";
3825 }
3826 std::cout << std::endl;
3827 #endif
3828
3829 } // endif: a pending matrix element was found
3830
3831 // Return
3832 return;
3833}
3834
3835
3836/***********************************************************************//**
3837 * @brief Allocate memory for new matrix elements
3838 *
3839 * @param[in] start Index of first allocated element.
3840 * @param[in] num Number of elements to be allocated.
3841 *
3842 * Inserts a memory allocation for 'num' elements at the index 'start'.
3843 * The new elements are filled with 0.0 and the corresponding row indices
3844 * are set to 0.
3845 *
3846 * NOTE: This method does not take care of updating 'm_colstart'. This has
3847 * to be done by the client.
3848 ***************************************************************************/
3849void GMatrixSparse::alloc_elements(int start, const int& num)
3850{
3851 // Dump header
3852 #if defined(G_DEBUG_SPARSE_MALLOC)
3853 std::cout << "GMatrixSparse::alloc_elements(start=" << start << ", num=" <<
3854 num << ")" << std::endl;
3855 std::cout << " Before allocation : " << m_elements << " " << m_alloc << std::endl;
3856 #endif
3857
3858 // Continue only if we need memory
3859 if (num > 0) {
3860
3861 // If start is after the end then append memory
3862 if (start > m_elements) {
3863 start = m_elements;
3864 }
3865
3866 // Determine the requested new logical size of the matrix
3867 int new_size = m_elements + num;
3868
3869 // Case A: the requested memory is already available, so just move the
3870 // data to make space for new elements and initialise the new cells
3871 if (new_size <= m_alloc) {
3872
3873 // Move up all elements after index to insert
3874 for (int i = m_elements - 1; i >= start; --i) {
3875 m_data[i+num] = m_data[i];
3876 m_rowinx[i+num] = m_rowinx[i];
3877 }
3878
3879 // Clear new elements (zero content for row 0)
3880 for (int i = start; i < start+num; ++i) {
3881 m_data[i] = 0.0;
3882 m_rowinx[i] = 0;
3883 }
3884
3885 // Update element counter
3886 m_elements += num;
3887
3888 } // endif: Case A: memory already existed
3889
3890 // Case B: more memory is needed, so allocate it, copy the existing
3891 // content, and initialise new cells
3892 else {
3893
3894 // Make sure that enough memory is allocated
3895 int new_propose = m_alloc + m_mem_block;
3896 m_alloc = (new_size > new_propose) ? new_size : new_propose;
3897
3898 // Allocate memory for new elements
3899 double* new_data = new double[m_alloc];
3900 int* new_rowinx = new int[m_alloc];
3901
3902 // Copy all elements before index to insert
3903 for (int i = 0; i < start; ++i) {
3904 new_data[i] = m_data[i];
3905 new_rowinx[i] = m_rowinx[i];
3906 }
3907
3908 // Clear new elements (zero content for row 0)
3909 for (int i = start; i < start+num; ++i) {
3910 new_data[i] = 0.0;
3911 new_rowinx[i] = 0;
3912 }
3913
3914 // Copy all elements after index to insert
3915 for (int i = start; i < m_elements; ++i) {
3916 new_data[i+num] = m_data[i];
3917 new_rowinx[i+num] = m_rowinx[i];
3918 }
3919
3920 // Delete old memory
3921 if (m_data != NULL) delete [] m_data;
3922 if (m_rowinx != NULL) delete [] m_rowinx;
3923
3924 // Update pointers to new memory and update element counter
3925 m_data = new_data;
3926 m_rowinx = new_rowinx;
3927 m_elements += num;
3928
3929 } // endelse: Case B: more memory was needed
3930
3931 } // endif: needed new memory
3932
3933 // Dump new memory size
3934 #if defined(G_DEBUG_SPARSE_MALLOC)
3935 std::cout << " After allocation .: " << m_elements << " " << m_alloc << std::endl;
3936 #endif
3937
3938 // Return
3939 return;
3940}
3941
3942
3943/***********************************************************************//**
3944 * @brief Free memory for obsolete matrix elements
3945 *
3946 * @param[in] start Index of first freed element.
3947 * @param[in] num Number of elements to be freed.
3948 *
3949 * Free memory for 'num' elements starting from index 'start'.
3950 *
3951 * NOTE: This method does not take care of updating 'm_colstart'. This has
3952 * to be done by the client.
3953 ***************************************************************************/
3954void GMatrixSparse::free_elements(const int& start, const int& num)
3955{
3956 // Dump header
3957 #if defined(G_DEBUG_SPARSE_MALLOC)
3958 std::cout << "GMatrixSparse::free_elements(start=" << start << ", num=" <<
3959 num << ")" << std::endl;
3960 #endif
3961
3962 // Continue only if we need to free memory and if start is within the
3963 // range
3964 if (num > 0 && start < m_elements) {
3965
3966 // Determine the requested new logical size of the matrix
3967 int new_size = m_elements - num;
3968
3969 // If there are no elements then simply delete all matrix elements ...
3970 if (new_size < 1) {
3971 if (m_data != NULL) delete [] m_data;
3972 if (m_rowinx != NULL) delete [] m_rowinx;
3973 m_data = NULL;
3974 m_rowinx = NULL;
3975 m_elements = 0;
3976 m_alloc = 0;
3977 }
3978
3979 // ... otherwise shrink the array
3980 else {
3981
3982 // Case A: If at least one entire memory block has been liberated then
3983 // physically shrink the matrix array
3984 if (m_alloc - new_size > m_mem_block) {
3985
3986 // Shrink, but leave a memory block for possible future filling
3987 m_alloc = new_size + m_mem_block;
3988
3989 // Allocate new memory
3990 double* new_data = new double[m_alloc];
3991 int* new_rowinx = new int[m_alloc];
3992
3993 // Copy all elements before the starting index
3994 for (int i = 0; i < start; ++i) {
3995 new_data[i] = m_data[i];
3996 new_rowinx[i] = m_rowinx[i];
3997 }
3998
3999 // Copy all elements after the starting index
4000 for (int i = start; i < new_size; ++i) {
4001 new_data[i] = m_data[i+num];
4002 new_rowinx[i] = m_rowinx[i+num];
4003 }
4004
4005 // Delete old memory
4006 if (m_data != NULL) delete [] m_data;
4007 if (m_rowinx != NULL) delete [] m_rowinx;
4008
4009 // Update pointers to new memory and update element counter
4010 m_data = new_data;
4011 m_rowinx = new_rowinx;
4012 m_elements = new_size;
4013
4014 } // endif: Case A: memory shrinkage performed
4015
4016 // Case B: we keep the memory and just move the elements
4017 else {
4018
4019 // Move all elements after the starting index
4020 for (int i = start; i < new_size; ++i) {
4021 m_data[i] = m_data[i+num];
4022 m_rowinx[i] = m_rowinx[i+num];
4023 }
4024
4025 // Update element counter
4026 m_elements = new_size;
4027
4028 } // endelse: Case B
4029
4030 } // endif: array shrinkage needed
4031 } // endif: needed new memory
4032
4033 // Return
4034 return;
4035}
4036
4037
4038/***********************************************************************//**
4039 * @brief Remove rows and columns with zeros
4040 *
4041 * @exception GException::runtime_error
4042 * All matrix elements are zero.
4043 *
4044 * Remove all rows and columns that contain only zeros from matrix. This
4045 * function is needed for compressed matrix factorisation. The resulting
4046 * matrix has reduced size (number of rows and columns) and dimensions
4047 * (number of elements). Note that the physical memory is not reduced by
4048 * the method.
4049 ***************************************************************************/
4051{
4052 // Dump header
4053 #if defined(G_DEBUG_SPARSE_COMPRESSION)
4054 std::cout << "GMatrixSparse::remove_zero_row_col" << std::endl;
4055 #endif
4056
4057 // Fill pending value in matrix
4058 fill_pending();
4059
4060 // Select non-zero rows and columns of matrix
4062
4063 // Stop if there is no compression
4064 if (m_rows == m_num_rowsel && m_cols == m_num_colsel) {
4065 return;
4066 }
4067
4068 // Throw exception if all matrix elements are zero
4069 if (m_num_rowsel < 1 || m_num_colsel < 1) {
4070 std::string msg = "All matrix elements are zero.";
4072 }
4073
4074 // Allocate row mapping array
4075 int* row_map = new int[m_rows];
4076
4077 // Setup row mapping array that maps original matrix rows into compressed
4078 // matrix rows. An entry of -1 indicates that the row should be dropped
4079 for (int row = 0; row < m_rows; ++row) {
4080 row_map[row] = -1;
4081 }
4082 for (int c_row = 0; c_row < m_num_rowsel; ++c_row) {
4083 row_map[m_rowsel[c_row]] = c_row;
4084 }
4085
4086 // Initialise pointers to compressed array
4087 double* d_data = m_data;
4088 int* d_rowinx = m_rowinx;
4089
4090 // Initialise column start of first column to zero
4091 m_colstart[0] = 0;
4092
4093 // Dump column start array
4094 #if defined(G_DEBUG_SPARSE_COMPRESSION)
4095 std::cout << " before compression (" << m_colstart[m_cols] << "):";
4096 for (int col = 0; col <= m_cols; ++col)
4097 std::cout << " " << m_colstart[col];
4098 std::cout << std::endl;
4099 #endif
4100
4101 // Loop over all columns of compressed matrix
4102 for (int c_col = 0; c_col < m_num_colsel; ++c_col) {
4103
4104 // Get index range for elements in original matrix
4105 int i_start = m_colstart[m_colsel[c_col]];
4106 int i_stop = m_colstart[m_colsel[c_col]+1];
4107
4108 // Initialise number of element counter for the compressed column
4109 int num = 0;
4110
4111 // Loop over all elements in original column
4112 for (int i = i_start; i < i_stop; ++i) {
4113 int c_row = row_map[m_rowinx[i]];
4114 if (c_row >= 0) {
4115 *d_data++ = m_data[i];
4116 *d_rowinx++ = c_row;
4117 num++;
4118 }
4119 }
4120
4121 // Update column stop
4122 m_colstart[c_col+1] = m_colstart[c_col] + num;
4123
4124 } // endfor: looped over all columns of compressed matrix
4125
4126 // Dump column start array
4127 #if defined(G_DEBUG_SPARSE_COMPRESSION)
4128 std::cout << " after compression (" << m_colstart[m_num_colsel] << ") :";
4129 for (int c_col = 0; c_col <= m_num_colsel; ++c_col)
4130 std::cout << " " << m_colstart[c_col];
4131 std::cout << std::endl;
4132 #endif
4133
4134 // Free row mapping array
4135 delete [] row_map;
4136
4137 // Update matrix size
4140
4141 // Return
4142 return;
4143}
4144
4145
4146/***********************************************************************//**
4147 * @brief Insert zero rows and columns
4148 *
4149 * @param[in] rows Number of rows.
4150 * @param[in] cols Number of columns.
4151 *
4152 * Insert zero rows and columns into matrix. Since for a sparse matrix
4153 * this does not require any allocation of additional memory, the data are
4154 * not moved by this function, but the pointers are re-arranged.
4155 ***************************************************************************/
4156void GMatrixSparse::insert_zero_row_col(const int& rows, const int& cols)
4157{
4158 // Dump header
4159 #if defined(G_DEBUG_SPARSE_COMPRESSION)
4160 std::cout << "GMatrixSparse::insert_zero_row_col(" << rows << "," << cols <<
4161 ")" << std::endl;
4162 #endif
4163
4164 // Fill pending value in matrix
4165 fill_pending();
4166
4167 // Stop if there is no insertion
4168 if (m_rows == rows && m_cols == cols) {
4169 return;
4170 }
4171
4172 // If row selection exists then restore row indices
4173 if (m_rowsel != NULL) {
4174 for (int i = 0; i < m_elements; ++i) {
4175 m_rowinx[i] = m_rowsel[m_rowinx[i]];
4176 }
4177 }
4178
4179 // Dump column start array
4180 #if defined(G_DEBUG_SPARSE_COMPRESSION)
4181 std::cout << " before restoration (" << m_colstart[m_num_colsel] << "):";
4182 for (int c_col = 0; c_col <= m_num_colsel; ++c_col)
4183 std::cout << " " << m_colstart[c_col];
4184 std::cout << std::endl;
4185 #endif
4186
4187 // If column selection exists then restore column counters
4188 if (m_colsel != NULL) {
4189
4190 // Start insertion from the last original column
4191 int col_stop = cols - 1;
4192
4193 // Loop over all compressed columns
4194 for (int c_col = m_num_colsel-1; c_col > 0; --c_col) {
4195 int col_start = m_colsel[c_col-1] + 1;
4196 int col_value = m_colstart[c_col];
4197 for (int col = col_start; col <= col_stop; ++col) {
4198 m_colstart[col] = col_value;
4199 }
4200 col_stop = col_start - 1;
4201 }
4202
4203 // Set first columns if they are not yet set
4204 for (int col = 0; col <= col_stop; ++col) {
4205 m_colstart[col] = 0;
4206 }
4207
4208 // Restore the number of elements
4209 m_colstart[cols] = m_elements;
4210 }
4211
4212 // Dump column start array
4213 #if defined(G_DEBUG_SPARSE_COMPRESSION)
4214 std::cout << " after restoration (" << m_colstart[cols] << ") :";
4215 for (int col = 0; col <= cols; ++col) {
4216 std::cout << " " << m_colstart[col];
4217 }
4218 std::cout << std::endl;
4219 #endif
4220
4221 // Update matrix size
4222 m_rows = rows;
4223 m_cols = cols;
4224
4225 // Return
4226 return;
4227}
4228
4229
4230/***********************************************************************//**
4231 * @brief Prepare mix of sparse columns
4232 *
4233 * @param[in] src1_row Row index array [0...src1_num-1] of first column.
4234 * @param[in] src1_num Number of elements in first column.
4235 * @param[in] src2_row Row index array [0...src2_num-1] of second column.
4236 * @param[in] src2_num Number of elements in second column.
4237 * @param[out] num_1 Number of elements only found in column 1.
4238 * @param[out] num_2 Number of elements only found in column 2.
4239 * @param[out] num_mix Number of elements found in both columns.
4240 *
4241 * This method prepares the mix of two sparse matrix columns into a single
4242 * column. It counts the number of elements in both columns that have the
4243 * same row and puts this number into @p num_mix. The number of elements
4244 * that are only present in the first column will be put in @p num_1, the
4245 * number of elements that are only present in the second column will be put
4246 * in @p num_2.
4247 ***************************************************************************/
4248void GMatrixSparse::mix_column_prepare(const int* src1_row, int src1_num,
4249 const int* src2_row, int src2_num,
4250 int* num_1, int* num_2, int* num_mix)
4251{
4252 // Initialise element counters
4253 *num_1 = 0;
4254 *num_2 = 0;
4255 *num_mix = 0;
4256
4257 // Initialise indices and row indices of both columns
4258 int inx_1 = 0; // Column 1 element index
4259 int inx_2 = 0; // Column 2 element index
4260 int row_1 = src1_row[inx_1]; // Column 1 first row index
4261 int row_2 = src2_row[inx_2]; // Column 2 first row index
4262
4263 // Count number of elements with same row or elements that exist only in
4264 // either the first or the second column while both columns contain still
4265 // elements
4266 while (inx_1 < src1_num && inx_2 < src2_num) {
4267
4268 // Case A: the element exist in both columns
4269 if (row_1 == row_2) {
4270 (*num_mix)++;
4271 inx_1++;
4272 inx_2++;
4273 if (inx_1 < src1_num) {
4274 row_1 = src1_row[inx_1];
4275 }
4276 if (inx_2 < src2_num) {
4277 row_2 = src2_row[inx_2];
4278 }
4279 }
4280
4281 // Case B: the element exists only in first column
4282 else if (row_1 < row_2) {
4283 (*num_1)++;
4284 inx_1++;
4285 if (inx_1 < src1_num) {
4286 row_1 = src1_row[inx_1];
4287 }
4288 }
4289
4290 // Case C: the element exists only in second column
4291 else {
4292 (*num_2)++;
4293 inx_2++;
4294 if (inx_2 < src2_num) {
4295 row_2 = src2_row[inx_2];
4296 }
4297 }
4298
4299 } // endwhile: counted elements
4300
4301 // At this point at least one column expired of elements. If there are
4302 // still elements remaining in the first column then count them now.
4303 if (inx_1 < src1_num) {
4304 *num_1 += (src1_num - inx_1);
4305 }
4306
4307 // If there are still elements remaining in the second column then count
4308 // them now.
4309 if (inx_2 < src2_num) {
4310 *num_2 += (src2_num - inx_2);
4311 }
4312
4313 // We're done
4314 return;
4315}
4316
4317
4318/***********************************************************************//**
4319 * @brief Mix of sparse columns
4320 *
4321 * @param[in] src1_data Data array [0...src1_num-1] of first column.
4322 * @param[in] src1_row Row index array [0...src1_num-1] of first column.
4323 * @param[in] src1_num Number of elements in first column.
4324 * @param[in] src2_data Data array [0...src2_num-1] of second column.
4325 * @param[in] src2_row Row index array [0...src2_num-1] of second column.
4326 * @param[in] src2_num Number of elements in second column.
4327 * @param[in] dst_data Data array [0...dst_num-1] of mixed column.
4328 * @param[in] dst_row Row index array [0...dst_num-1] of mixed column.
4329 * @param[out] dst_num Number of elements in mixed column.
4330 *
4331 * This method mixes two sparse matrix columns into a single column.
4332 ***************************************************************************/
4333void GMatrixSparse::mix_column(const double* src1_data,
4334 const int* src1_row,
4335 int src1_num,
4336 const double* src2_data,
4337 const int* src2_row,
4338 int src2_num,
4339 double* dst_data,
4340 int* dst_row,
4341 int* dst_num)
4342{
4343 // Initialise indices and row indices of both columns
4344 int inx = 0; // Mixed column element index
4345 int inx_1 = 0; // Column 1 element index
4346 int inx_2 = 0; // Column 2 element index
4347 int row_1 = src1_row[inx_1]; // Current row in column 1
4348 int row_2 = src2_row[inx_2]; // Current row in column 2
4349
4350 // Mix elements of both columns while both contain still elements
4351 while (inx_1 < src1_num && inx_2 < src2_num) {
4352
4353 // Case A: the element exists in both columns, so we add the values
4354 if (row_1 == row_2) {
4355 dst_data[inx] = src1_data[inx_1++] + src2_data[inx_2++];
4356 dst_row[inx] = row_1;
4357 if (inx_1 < src1_num) {
4358 row_1 = src1_row[inx_1];
4359 }
4360 if (inx_2 < src2_num) {
4361 row_2 = src2_row[inx_2];
4362 }
4363 }
4364
4365 // Case B: the element exists only in first column, so we copy the
4366 // element from the first column
4367 else if (row_1 < row_2) {
4368 dst_data[inx] = src1_data[inx_1++];
4369 dst_row[inx] = row_1;
4370 if (inx_1 < src1_num) {
4371 row_1 = src1_row[inx_1];
4372 }
4373 }
4374
4375 // Case C: the element exists only in second column, so we copy the
4376 // element from the second column
4377 else {
4378 dst_data[inx] = src2_data[inx_2++];
4379 dst_row[inx] = row_2;
4380 if (inx_2 < src2_num) {
4381 row_2 = src2_row[inx_2];
4382 }
4383 }
4384
4385 // Update the destination index since we added a element
4386 inx++;
4387
4388 } // endwhile: mixing
4389
4390 // At this point either the first or the second column expired of elements
4391 // In the case that there are still elements remaining in the first column
4392 // we add them now ...
4393 for (int i = inx_1; i < src1_num; ++i, ++inx) {
4394 dst_data[inx] = src1_data[i];
4395 dst_row[inx] = src1_row[i];
4396 }
4397
4398 // ... or in the case that there are still elements remaining in the second
4399 // column we add them now
4400 for (int i = inx_2; i < src2_num; ++i, ++inx) {
4401 dst_data[inx] = src2_data[i];
4402 dst_row[inx] = src2_row[i];
4403 }
4404
4405 // Now store the number of elements in the mixed column
4406 *dst_num = inx;
4407
4408 // We're done
4409 return;
4410}
4411
4412
4413/***********************************************************************//**
4414 * @brief Insert row in matrix
4415 *
4416 * @param[in] row Matrix row [0,...,row()[.
4417 * @param[in] vector Vector.
4418 * @param[in] add Add vector to existing elements?
4419 *
4420 * @exception GException::out_of_range
4421 * Invalid matrix row specified.
4422 * @exception GException::invalid_argument
4423 * Vector size incompatible with number of matrix columns.
4424 *
4425 * @todo Remove elements that are empty after addition
4426 ***************************************************************************/
4427void GMatrixSparse::insert_row(const int& row,
4428 const GVector& vector,
4429 const bool& add)
4430{
4431 // Raise an exception if the row index is invalid
4432 #if defined(G_RANGE_CHECK)
4433 if (row < 0 || row >= m_rows) {
4434 throw GException::out_of_range(G_SET_ROW, "Matrix row", row, m_rows);
4435 }
4436 #endif
4437
4438 // Raise an exception if the vector length is invalid
4439 if (vector.size() != m_cols) {
4440 std::string msg = "Vector size "+gammalib::str(vector.size())+
4441 " does not match "+gammalib::str(m_cols)+" matrix "
4442 "columns. Please specify a vector of size "+
4443 gammalib::str(m_cols)+".";
4445 }
4446
4447 // Fill pending element into matrix
4448 fill_pending();
4449
4450 // Determine indices of new columns that need to be allocated and set
4451 // or add all vector elements that do not need a new memory allocation
4452 std::vector<int> new_columns;
4453 std::vector<int> k_insert;
4454 for (int i = m_cols-1; i >= 0; --i) {
4455 if (vector[i] != 0.0) {
4456 int k = m_colstart[i];
4457 int istop = m_colstart[i+1];
4458 bool add_column = true;
4459 for (; k < istop; ++k) {
4460 if (m_rowinx[k] == row) {
4461 if (add) {
4462 m_data[k] += vector[i];
4463 }
4464 else {
4465 m_data[k] = vector[i];
4466 }
4467 add_column = false;
4468 break;
4469 }
4470 else if (m_rowinx[k] > row) {
4471 break;
4472 }
4473 }
4474 if (add_column) {
4475 new_columns.push_back(i);
4476 k_insert.push_back(k);
4477 }
4478 }
4479 }
4480
4481 // If new columns are needed then allocate new memory and insert
4482 // new columns
4483 if (!new_columns.empty()) {
4484
4485 // Determine the requested new logical size of the matrix
4486 int new_size = m_elements + new_columns.size();
4487
4488 // Initialise pointers to destination
4489 bool new_memory = false;
4490 double* new_data = m_data;
4491 int* new_rowinx = m_rowinx;
4492
4493 // If requested size is smaller than allocated size then allocate
4494 // new memory
4495 if (new_size > m_alloc) {
4496
4497 // Propose a new memory size
4498 int new_propose = m_alloc + m_mem_block;
4499
4500 // Make sure that enough memory is allocated
4501 m_alloc = (new_size > new_propose) ? new_size : new_propose;
4502
4503 // Allocate memory for new elements
4504 new_data = new double[m_alloc];
4505 new_rowinx = new int[m_alloc];
4506
4507 // Signal that new memory was allocated
4508 new_memory = true;
4509
4510 } // endif: memory allocation required
4511
4512 // Loop over all new columns
4513 int n_insert = new_columns.size();
4514 int k_last = m_elements;
4515 for (int i = 0; i < new_columns.size(); ++i) {
4516
4517 // Move data back by n_insert positions
4518 for (int k = k_insert[i]; k < k_last; ++k) {
4519 new_data[k+n_insert] = m_data[k];
4520 new_rowinx[k+n_insert] = m_rowinx[k];
4521 }
4522
4523 // Insert vector element just before the block that was moved
4524 // back
4525 new_data[k_insert[i]+n_insert-1] = vector[new_columns[i]];
4526 new_rowinx[k_insert[i]+n_insert-1] = row;
4527
4528 // Update number of positions to move back and end of block
4529 n_insert--;
4530 k_last = k_insert[i];
4531
4532 } // endfor: looped over all new columns
4533
4534 // Update column start and number of elements
4535 int i_insert = 0;
4536 n_insert = new_columns.size();
4537 for (int i = m_cols; i > 0; --i) {
4538 if (new_columns[i_insert] == i) {
4539 n_insert--;
4540 i_insert++;
4541 if (n_insert < 1) {
4542 break;
4543 }
4544 }
4545 m_colstart[i] += n_insert;
4546 }
4547
4548 // Update number of elements
4549 m_elements = new_size;
4550
4551 // If memory was allocated then free old memory and attach new
4552 // memory
4553 if (new_memory) {
4554
4555 // Delete old memory
4556 if (m_data != NULL) delete [] m_data;
4557 if (m_rowinx != NULL) delete [] m_rowinx;
4558
4559 // Update pointers to new memory and update element counter
4560 m_data = new_data;
4561 m_rowinx = new_rowinx;
4562
4563 } // endif: memory was allocated
4564
4565 } // endif: new columns were needed
4566
4567 // Return
4568 return;
4569}
4570
4571
4572/*==========================================================================
4573 = =
4574 = Friend functions =
4575 = =
4576 ==========================================================================*/
4577
4578/***********************************************************************//**
4579 * @brief cs_symperm
4580 *
4581 * @param[in] matrix Matrix.
4582 * @param[in] pinv TBD.
4583 *
4584 * Returns matrix(p,p) where matrix and matrix(p,p) are symmetric the upper
4585 * part stored.
4586 ***************************************************************************/
4587GMatrixSparse cs_symperm(const GMatrixSparse& matrix, const int* pinv)
4588{
4589 // Declare loop variables
4590 int i, j, p, q, i2, j2;
4591
4592 // Assign matrix attributes
4593 int n = matrix.m_cols;
4594 int* Ap = matrix.m_colstart;
4595 int* Ai = matrix.m_rowinx;
4596 double* Ax = matrix.m_data;
4597
4598 // Allocate result matrix
4599 GMatrixSparse C(n, n, Ap[n]);
4600
4601 // Allocate and initialise workspace
4602 int wrk_size = n;
4603 int* wrk_int = new int[wrk_size];
4604 for (i = 0; i < wrk_size; ++i) {
4605 wrk_int[i] = 0;
4606 }
4607
4608 // Assign result matrix attributes
4609 int* Cp = C.m_colstart;
4610 int* Ci = C.m_rowinx;
4611 double* Cx = C.m_data;
4612
4613 // Count entries in each column of C
4614 for (j = 0; j < n; j++) {
4615
4616 // Column j of A is column j2 of C
4617 j2 = pinv ? pinv[j] : j;
4618
4619 // Loop over entries in column j
4620 for (p = Ap[j]; p < Ap[j+1]; p++) {
4621 i = Ai [p];
4622 if (i > j) continue; // skip lower triangular part of A
4623 i2 = pinv ? pinv[i] : i; // row i of A is row i2 of C
4624 wrk_int[G_MAX(i2, j2)]++; // column count of C
4625 }
4626 }
4627
4628 // Compute column pointers of C
4629 cs_cumsum(Cp, wrk_int, n);
4630
4631 // Loop over all columns of A
4632 for (j = 0 ; j < n ; j++) {
4633
4634 // Column j of A is column j2 of C
4635 j2 = pinv ? pinv[j] : j;
4636
4637 // Loop over entries in column j
4638 for (p = Ap[j]; p < Ap[j+1]; p++) {
4639 i = Ai [p] ;
4640 if (i > j) continue; // skip lower triangular part of A
4641 i2 = pinv ? pinv[i] : i; // row i of A is row i2 of C
4642 Ci[q = wrk_int[G_MAX(i2,j2)]++] = G_MIN(i2,j2);
4643 if (Cx) Cx[q] = Ax[p];
4644 }
4645 }
4646
4647 // Free workspace
4648 delete [] wrk_int;
4649
4650 // Rectify the number of elements in matrix C
4651 C.free_elements(Cp[n], (C.m_elements-Cp[n]));
4652
4653 // Return result
4654 return C;
4655}
4656
4657
4658/***************************************************************************
4659 * @brief Compute transpose matrix
4660 *
4661 * @param[in] matrix Matrix.
4662 * @param[in] values Flag that signals if values should be copied.
4663 *
4664 * The flag 'values' allows to avoid copying the actual data values. This
4665 * allows to perform a logical matrix transposition, as needed by the
4666 * symbolic matrix analysis class.
4667 *
4668 * Note that this method does not support pending elements (they have to be
4669 * filled before).
4670 *
4671 * The method does nothing on empty matrices.
4672 ***************************************************************************/
4673GMatrixSparse cs_transpose(const GMatrixSparse& matrix, int values)
4674{
4675 // Declare and allocate result matrix
4676 GMatrixSparse result(matrix.m_cols, matrix.m_rows, matrix.m_elements);
4677
4678 // Transpose only if matrix is not empty
4679 if (!matrix.is_empty()) {
4680
4681 // Allocate and initialise workspace
4682 int wrk_size = matrix.m_rows;
4683 int* wrk_int = new int[wrk_size];
4684 for (int i = 0; i < wrk_size; ++i) {
4685 wrk_int[i] = 0;
4686 }
4687
4688 // Setup the number of non-zero elements in each row
4689 // for (p = 0 ; p < Ap [n] ; p++) w [Ai [p]]++ ;
4690 // Ap[n] = m.m_colstart[col]
4691 // n = m.m_cols
4692 // Ai[p] = m.m_rowinx[p]
4693 for (int p = 0; p < matrix.m_colstart[matrix.m_cols]; ++p) {
4694 wrk_int[matrix.m_rowinx[p]]++;
4695 }
4696
4697 // Set row pointers. To use a GSparseSymbolic function we have to
4698 // allocate and object (but this does not take memory)
4699 cs_cumsum(result.m_colstart, wrk_int, matrix.m_rows);
4700
4701 // Case A: Normal transponse, including assignment of values
4702 if (values) {
4703 for (int col = 0; col < matrix.m_cols; ++col) {
4704 for (int p = matrix.m_colstart[col]; p < matrix.m_colstart[col+1] ; ++p) {
4705 int i = wrk_int[matrix.m_rowinx[p]]++;
4706 result.m_rowinx[i] = col;
4707 result.m_data[i] = matrix.m_data[p] ;
4708 }
4709 }
4710 }
4711
4712 // Case B: Logical transponse, no assignment of values is performed
4713 else {
4714 for (int col = 0; col < matrix.m_cols; ++col) {
4715 for (int p = matrix.m_colstart[col]; p < matrix.m_colstart[col+1] ; ++p) {
4716 result.m_rowinx[wrk_int[matrix.m_rowinx[p]]++] = col;
4717 }
4718 }
4719 }
4720
4721 // Free workspace
4722 delete [] wrk_int;
4723
4724 } // endif: matrix was not empty
4725
4726 // Return transponse matrix
4727 return result;
4728}
4729
4730
4731/***********************************************************************//**
4732 * @brief cs_cumsum
4733 *
4734 * @param[out] p Integer array (n+1 elements).
4735 * @param[in] c Integer array (n elements).
4736 * @param[in] n Number of elements.
4737 *
4738 * Evaluate p[0..n] = cumulative sum of c[0..n-1]
4739 ***************************************************************************/
4740double cs_cumsum(int* p, int* c, int n)
4741{
4742 // Signal error if one of the input pointers is NULL
4743 if (!p || !c) return (-1);
4744
4745 // Initialise sums (integer and double)
4746 int nz = 0;
4747 double nz2 = 0.0;
4748
4749 // Evaluate p[0..n] = cumulative sum of c[0..n-1]
4750 for (int i = 0; i < n; ++i) {
4751 p[i] = nz ;
4752 nz += c[i];
4753 nz2 += c[i]; // also in double to avoid int overflow
4754 c[i] = p[i]; // also copy p[0..n-1] back into c[0..n-1]
4755 }
4756 p[n] = nz ;
4757
4758 // Return cumulative sum of c[0..n-1]
4759 return nz2;
4760}
#define G_AT
Exception handler interface definition.
#define G_CHOL_SOLVE
#define G_COLFILL_FLUSH
#define G_COLFILL_SET_COLUMN
#define G_ADD_TO_COLUMN3
#define G_MIN(a, b)
#define G_MAX(a, b)
#define G_COLUMN_TO_VECTOR1
GMatrixSparse cs_transpose(const GMatrixSparse &matrix, int values)
#define G_REMOVE_ZERO
#define G_ADD_TO_COLUMN1
#define G_GET_INDEX
#define G_COLUMN_TO_VECTOR2
double cs_cumsum(int *p, int *c, int n)
cs_cumsum
#define G_STACK_PUSH1
#define G_INSERT_ROW
GMatrixSparse cs_symperm(const GMatrixSparse &matrix, const int *pinv)
cs_symperm
#define G_MULTIPLY_COLUMN
#define G_SET_COLUMN2
#define G_STACK_PUSH2
Sparse matrix class definition.
#define G_SPARSE_MATRIX_DEFAULT_MEM_BLOCK
#define G_SPARSE_MATRIX_DEFAULT_STACK_SIZE
Symmetric matrix class definition.
#define G_CONSTRUCTOR
Definition GMatrix.cpp:41
#define G_OP_ADD
Definition GMatrix.cpp:43
#define G_EXTRACT_ROW
Definition GMatrix.cpp:47
#define G_EXTRACT_COLUMN
Definition GMatrix.cpp:49
#define G_OP_MUL_VEC
Definition GMatrix.cpp:42
#define G_OP_MAT_MUL
Definition GMatrix.cpp:45
#define G_SET_ROW
Definition GMatrix.cpp:48
#define G_SET_COLUMN
Definition GMatrix.cpp:50
#define G_OP_SUB
Definition GMatrix.cpp:44
Generic matrix class definition.
Sparse matrix numeric analysis class definition.
Sparse matrix symbolic analysis class definition.
Gammalib tools definition.
GChatter
Definition GTypemaps.hpp:33
@ SILENT
Definition GTypemaps.hpp:34
Sparce vector class interface definition.
GVector iperm(const GVector &vector, const int *p)
Computes vector inverse permutation.
Definition GVector.cpp:1106
GVector perm(const GVector &vector, const int *p)
Computes vector permutation.
Definition GVector.cpp:1079
Vector class interface definition.
Abstract matrix base class interface definition.
const int & rows(void) const
Return number of matrix rows.
int m_num_rowsel
Number of selected rows (for compressed decomposition)
const int & size(void) const
Return number of matrix elements.
std::string print_col_compression(const GChatter &chatter=NORMAL) const
Print column compression scheme if it exists.
int m_cols
Number of columns.
int m_rows
Number of rows.
const int & columns(void) const
Return number of matrix columns.
virtual GMatrixBase & operator=(const GMatrixBase &matrix)
Assignment operator.
int * m_colsel
Column selection (for compressed decomposition)
double * m_data
Matrix data.
bool is_empty(void) const
Check if matrix is empty.
int m_num_colsel
Number of selected columns (for compressed decomposition)
std::string print_row_compression(const GChatter &chatter=NORMAL) const
Print row compression scheme if it exists.
void select_non_zero(void)
Setup compressed matrix factorisation.
std::string print_elements(const GChatter &chatter=NORMAL, const int &num=15) const
Print all matrix elements.
int m_alloc
Size of allocated matrix memory.
int * m_colstart
Column start indices (m_cols+1)
int * m_rowsel
Row selection (for compressed decomposition)
int m_elements
Number of elements stored in matrix.
Sparse matrix class interface definition.
virtual GVector column(const int &column) const
Extract column as vector from matrix.
double ** m_columns_data
Data for all columns.
GMatrixSparse transpose(void) const
Return transposed matrix.
void stack_destroy(void)
Destroy matrix stack.
virtual void add_to_column(const int &column, const GVector &vector)
Add vector column into matrix.
virtual void clear(void)
Clear matrix.
void free_colfill_members(void) const
Delete quick column filling class members.
void stack_init(const int &size=0, const int &entries=0)
Initialises matrix filling stack.
void column_to_vector(const int &column, GVector *vector) const
Extract matrix into vector.
virtual GMatrixSparse * clone(void) const
Clone matrix.
void free_members(void)
Delete class members.
GSparseSymbolic * m_symbolic
Holds GSparseSymbolic object after decomposition.
friend class GSparseNumeric
virtual std::string print(const GChatter &chatter=NORMAL) const
Print matrix.
virtual ~GMatrixSparse(void)
Destructor.
GMatrixSparse abs(void) const
Return absolute of matrix.
void init_members(void)
Initialise class mambers.
void insert_zero_row_col(const int &rows, const int &cols)
Insert zero rows and columns.
GMatrixSparse cholesky_invert(const bool &compress=true) const
Invert matrix using a Cholesky decomposition.
virtual double fill(void) const
Returns fill of matrix.
void init_stack_members(void)
Initialise fill stack.
void remove_zero_row_col(void)
Remove rows and columns with zeros.
GMatrixSparse invert(void) const
Return inverted matrix.
friend GMatrixSparse cs_transpose(const GMatrixSparse &matrix, int values)
int m_mem_block
Memory block to be allocated at once.
virtual GMatrixSparse & operator+=(const GMatrixSparse &matrix)
Unary matrix addition operator.
virtual bool operator!=(const GMatrixSparse &matrix) const
Non-equality operator.
GMatrixSparse cholesky_decompose(const bool &compress=true) const
Return Cholesky decomposition.
virtual double & operator()(const int &row, const int &column)
Return reference to matrix element.
virtual double sum(void) const
Sum matrix elements.
int ** m_columns_rowinx
Row indices for all columns.
void mix_column(const double *src1_data, const int *src1_row, int src1_num, const double *src2_data, const int *src2_row, int src2_num, double *dst_data, int *dst_row, int *dst_num)
Mix of sparse columns.
GVector solve(const GVector &vector) const
Solves linear matrix equation.
int m_stack_max_entries
Maximum number of entries in the stack.
virtual double min(void) const
Return minimum matrix element.
int * m_stack_colinx
Column index for each entry [m_stack_entries].
virtual GVector row(const int &row) const
Extract row as vector from matrix.
double m_zero
The zero element (needed for data access)
friend class GSparseSymbolic
void multiply_column(const int &column, const GVector &vector)
Multiply matrix column with vector.
double * m_stack_values
Stack push double buffer [m_cols].
GVector cholesky_solver(const GVector &vector, const bool &compress=true) const
Cholesky solver.
void stack_flush(void)
Flush matrix stack.
GSparseNumeric * m_numeric
Holds GSparseNumeric object after decomposition.
int m_stack_size
Maximum number of elements in the stack.
int * m_stack_rowinx
Stack row indices [m_stack_size].
int m_fill_col
Column into which element needs to be filled.
int * m_rowinx
Row-indices of all elements.
void alloc_members(const int &rows, const int &cols, const int &elements=0)
Allocate matrix.
void free_stack_members(void)
Delete fill-stack class members.
void colfill_set_column(const int &column, const GVector &vector)
Set column for column filling.
int * m_columns_length
Length of all columns.
void colfill_flush(void)
Flush column filling memory into sparse matrix.
virtual GMatrixSparse operator-(void) const
Negate matrix elements.
void colfill_init(void) const
Initialise memory for column filling.
int m_stack_entries
Number of entries in the stack.
virtual double & at(const int &row, const int &column)
Return reference to matrix element.
int stack_push_column(const GVector &vector, const int &col)
Push a vector column on the matrix stack.
virtual GVector operator*(const GVector &vector) const
Vector multiplication.
GMatrixSparse(void)
Void matrix constructor.
virtual double max(void) const
Return maximum matrix element.
void alloc_elements(int start, const int &num)
Allocate memory for new matrix elements.
virtual GMatrixSparse & operator-=(const GMatrixSparse &matrix)
Unary matrix subtraction operator.
int * m_stack_start
Start in stack for each entry [m_stack_entries+1].
virtual GMatrixSparse & operator=(const GMatrixSparse &matrix)
Matrix assignment operator.
int m_fill_row
Row into which element needs to be filled.
void mix_column_prepare(const int *src1_row, int src1_num, const int *src2_row, int src2_num, int *num_1, int *num_2, int *num_mix)
Prepare mix of sparse columns.
void free_elements(const int &start, const int &num)
Free memory for obsolete matrix elements.
virtual GMatrixSparse & operator*=(const GMatrixSparse &matrix)
Unary matrix multiplication operator.
double m_fill_val
Element to be filled.
double * m_stack_data
Stack data [m_stack_size].
int * m_stack_rows
Stack push integer working array [m_cols].
void copy_members(const GMatrixSparse &m)
Copy class members.
void insert_row(const int &row, const GVector &vector, const bool &add)
Insert row in matrix.
int get_index(const int &row, const int &column) const
Determines element index for (row,column)
virtual bool operator==(const GMatrixSparse &matrix) const
Equalty operator.
virtual void add_to_row(const int &row, const GVector &vector)
Add row to matrix elements.
int * m_stack_work
Stack flush integer working array [m_cols].
void fill_pending(void)
Fills pending matrix element.
Symmetric matrix class interface definition.
virtual GVector column(const int &column) const
Extract column as vector from matrix.
Generic matrix class definition.
Definition GMatrix.hpp:79
virtual GVector column(const int &column) const
Extract column as vector from matrix.
Definition GMatrix.cpp:718
Sparse matrix numeric analysis class.
GMatrixSparse * m_L
void cholesky_numeric_analysis(const GMatrixSparse &m, const GSparseSymbolic &s)
Sparse matrix symbolic analysis class.
int * m_pinv
Inverse row permutation for QR, fill reduce permutation for Cholesky.
void cholesky_symbolic_analysis(int order, const GMatrixSparse &m)
Sparse vector class.
int m_elements
Number of elements in vector.
int * m_inx
Index array.
void alloc_members(const int &alloc)
Allocate memory for elements.
int m_alloc
Number of allocated elements.
const int & size(void) const
Return full size of sparse vector.
const int & inx(const int &index) const
Return inx for sparse vector element.
int m_num
Vector size.
const int & elements(void) const
Return number of elements in sparse vector.
double * m_data
Data array.
int m_colinx
Column index.
Vector class.
Definition GVector.hpp:46
const int & size(void) const
Return size of vector.
Definition GVector.hpp:180
int non_zeros(void) const
Returns number of non-zero elements in vector.
Definition GVector.cpp:627
std::string parformat(const std::string &s, const int &indent=0)
Convert string in parameter format.
Definition GTools.cpp:1162
std::string str(const unsigned short int &value)
Convert unsigned short integer value into string.
Definition GTools.cpp:508