1 /* Ergo, version 3.8, a program for linear scaling electronic structure
2  * calculations.
3  * Copyright (C) 2019 Elias Rudberg, Emanuel H. Rubensson, Pawel Salek,
4  * and Anastasia Kruchinina.
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  * Primary academic reference:
20  * Ergo: An open-source program for linear-scaling electronic structure
21  * calculations,
22  * Elias Rudberg, Emanuel H. Rubensson, Pawel Salek, and Anastasia
23  * Kruchinina,
24  * SoftwareX 7, 107 (2018),
25  * <http://dx.doi.org/10.1016/j.softx.2018.03.005>
26  *
27  * For further information about Ergo, see <http://www.ergoscf.org>.
28  */
29 
30 /** @file MatrixSymmetric.h Symmetric matrix class
31  *
32  * Copyright(c) Emanuel Rubensson 2006
33  *
34  * @author Emanuel Rubensson  @a responsible @a author
35  * @date January 2006
36  *
37  */
38 #ifndef MAT_MatrixSymmetric
39 #define MAT_MatrixSymmetric
40 
41 #include <algorithm>
42 
43 #include "MatrixBase.h"
44 #include "Interval.h"
45 #include "LanczosLargestMagnitudeEig.h"
46 #include "mat_utils.h"
47 #include "truncation.h"
48 
49 namespace mat {
50   /** Symmetric matrix
51    *
52    *
53    * This class belongs to the matrix API
54    *
55    * The matrix is stored in the upper triangle.
56    *
57    * Treal: Type for real numbers
58    *
59    * Tmatrix: The matrix class
60    *
61    * @see MatrixBase
62    * @see MatrixGeneral
63    * @see MatrixTriangular
64    *
65    *
66    */
67   template<typename Treal, typename Tmatrix>
68     class MatrixSymmetric : public MatrixBase<Treal, Tmatrix> {
69   public:
70     typedef VectorGeneral<Treal, typename Tmatrix::VectorType> VectorType;
71     typedef Treal real;
72 
MatrixSymmetric()73     MatrixSymmetric()
74       :MatrixBase<Treal, Tmatrix>() {} /**< Default constructor  */
75       /* In the code we are using std::vector<MatrixSymmetric> which in the c++ standard before c++11 requires move operation like T x_new = x which calls implicitly the copy constructor. To make it work with g++ versions without c++11 support we remove the keyword explicit. */
76 #if __cplusplus >= 201103L
MatrixSymmetric(const MatrixSymmetric<Treal,Tmatrix> & symm)77     explicit MatrixSymmetric(const MatrixSymmetric<Treal, Tmatrix>& symm)
78       :MatrixBase<Treal, Tmatrix>(symm) {} /**< Copy constructor  */
79 #else
MatrixSymmetric(const MatrixSymmetric<Treal,Tmatrix> & symm)80     MatrixSymmetric(const MatrixSymmetric<Treal, Tmatrix>& symm)
81       :MatrixBase<Treal, Tmatrix>(symm) {} /**< Copy constructor  */
82 #endif
MatrixSymmetric(const XY<Treal,MatrixSymmetric<Treal,Tmatrix>> & sm)83     explicit MatrixSymmetric(const XY<Treal, MatrixSymmetric<Treal, Tmatrix> >& sm)
84       :MatrixBase<Treal, Tmatrix>() { *this = sm.A * sm.B; }
MatrixSymmetric(const MatrixGeneral<Treal,Tmatrix> & matr)85     explicit MatrixSymmetric(const MatrixGeneral<Treal, Tmatrix>& matr)
86       :MatrixBase<Treal, Tmatrix>(matr) {
87       this->matrixPtr->nosymToSym();
88     } /**< 'Copy from normal matrix' - constructor  */
89 
90 
91 #if 0
92     template<typename Tfull>
93       inline void assign_from_full
94       (Tfull const* const fullmatrix,
95        int const totnrows, int const totncols) {
96        assert(totnrows == totncols);
97       this->matrixPtr->assign_from_full(fullmatrix, totnrows, totncols);
98       this->matrixPtr->nosym_to_sym();
99     }
100     inline void assign_from_full
101       (Treal const* const fullmatrix,
102        int const totnrows, int const totncols) {
103       assert(totnrows == totncols);
104       this->matrixPtr->assign_from_full(fullmatrix, totnrows, totncols);
105       this->matrixPtr->nosym_to_sym();
106     }
107 #endif
108 
assignFromFull(std::vector<Treal> const & fullMat)109     inline void assignFromFull
110       (std::vector<Treal> const & fullMat) {
111       assert((int)fullMat.size() == this->get_nrows() * this->get_ncols());
112       this->matrixPtr->assignFromFull(fullMat);
113       this->matrixPtr->nosymToSym();
114     }
115 
assignFromFull(std::vector<Treal> const & fullMat,std::vector<int> const & rowPermutation,std::vector<int> const & colPermutation)116     inline void assignFromFull
117       (std::vector<Treal> const & fullMat,
118        std::vector<int> const & rowPermutation,
119        std::vector<int> const & colPermutation) {
120       assert((int)fullMat.size() == this->get_nrows() * this->get_ncols());
121       std::vector<int> rowind(fullMat.size());
122       std::vector<int> colind(fullMat.size());
123       int ind = 0;
124       for (int col = 0; col < this->get_ncols(); ++col)
125 	for (int row = 0; row < this->get_nrows(); ++row) {
126 	  rowind[ind] = row;
127 	  colind[ind] = col;
128 	  ++ind;
129 	}
130       this->assign_from_sparse(rowind,
131 			       colind,
132 			       fullMat,
133 			       rowPermutation,
134 			       colPermutation);
135       this->matrixPtr->nosymToSym();
136     }
137 
fullMatrix(std::vector<Treal> & fullMat)138     inline void fullMatrix(std::vector<Treal> & fullMat) const {
139       this->matrixPtr->syFullMatrix(fullMat);
140     }
141     /**< Save matrix as full matrix.
142      * Whole matrix is written in columnwise order.
143      * Both lower and upper triangle.
144      * NOTE that no permutation is used in this operation.
145      */
146 
fullMatrix(std::vector<Treal> & fullMat,std::vector<int> const & rowInversePermutation,std::vector<int> const & colInversePermutation)147     inline void fullMatrix
148       (std::vector<Treal> & fullMat,
149        std::vector<int> const & rowInversePermutation,
150        std::vector<int> const & colInversePermutation) const {
151       std::vector<int> rowind;
152       std::vector<int> colind;
153       std::vector<Treal> values;
154       get_all_values(rowind, colind, values,
155 		     rowInversePermutation,
156 		     colInversePermutation);
157       fullMat.assign(this->get_nrows()*this->get_ncols(),0);
158       assert(rowind.size() == values.size());
159       assert(rowind.size() == colind.size());
160       for (unsigned int ind = 0; ind < values.size(); ++ind) {
161 	assert(rowind[ind] + this->get_nrows() * colind[ind] <
162 	       this->get_nrows()*this->get_ncols());
163 	fullMat[rowind[ind] + this->get_nrows() * colind[ind]] =
164 	  values[ind];
165 	fullMat[colind[ind] + this->get_nrows() * rowind[ind]] =
166 	  values[ind];
167       }
168     }
169     /**< Save matrix as full matrix.
170      * Whole matrix is written in columnwise order.
171      * Both lower and upper triangle.
172      * Permutation is used.
173      */
174 
assign_from_sparse(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> const & values)175     inline void assign_from_sparse
176       (std::vector<int> const & rowind,
177        std::vector<int> const & colind,
178        std::vector<Treal> const & values) {
179       this->matrixPtr->syAssignFromSparse(rowind, colind, values);
180     }
181     /**< Assign from sparse matrix given by three vectors.
182      * The vectors contain row indices, column indices and values.
183      * The indices start at zero.
184      * The elements to be added must be given in upper triangluar storage.
185      * Information about sizes and blocks for rows as well as columns
186      * must also be given.
187      * Assumes that sizes and blocks are already set.
188      * @warning All indexing start at zero.
189      */
190 
191 
assign_from_sparse(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> const & values,SizesAndBlocks const & newRows,SizesAndBlocks const & newCols)192     inline void assign_from_sparse
193       (std::vector<int> const & rowind,
194        std::vector<int> const & colind,
195        std::vector<Treal> const & values,
196        SizesAndBlocks const & newRows,
197        SizesAndBlocks const & newCols) {
198       this->resetSizesAndBlocks(newRows, newCols);
199       this->matrixPtr->syAssignFromSparse(rowind, colind, values);
200     }
201     /**< Assign from sparse matrix given by three vectors.
202      * The vectors contain row indices, column indices and values.
203      * The indices start at zero.
204      * The elements to be added must be given in upper triangluar storage.
205      * Information about sizes and blocks for rows as well as columns
206      * must also be given.
207      * @warning All indexing start at zero.
208      */
209 
assign_from_sparse(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> const & values,std::vector<int> const & rowPermutation,std::vector<int> const & colPermutation)210     inline void assign_from_sparse
211       (std::vector<int> const & rowind,
212        std::vector<int> const & colind,
213        std::vector<Treal> const & values,
214        std::vector<int> const & rowPermutation,
215        std::vector<int> const & colPermutation) {
216       std::vector<int> newRowind;
217       std::vector<int> newColind;
218       this->getPermutedAndSymmetrized(rowind, rowPermutation, newRowind,
219 				      colind, colPermutation, newColind);
220 
221       this->matrixPtr->syAssignFromSparse(newRowind, newColind, values);
222     }
223     /**< Same as above, except taking two additional arguments
224      *   specifying the permutation of rows and columns.
225      *   Also, assuming that sizes and blocks are already set.
226      */
227 
assign_from_sparse(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> const & values,SizesAndBlocks const & newRows,SizesAndBlocks const & newCols,std::vector<int> const & rowPermutation,std::vector<int> const & colPermutation)228     inline void assign_from_sparse
229       (std::vector<int> const & rowind,
230        std::vector<int> const & colind,
231        std::vector<Treal> const & values,
232        SizesAndBlocks const & newRows,
233        SizesAndBlocks const & newCols,
234        std::vector<int> const & rowPermutation,
235        std::vector<int> const & colPermutation) {
236       this->resetSizesAndBlocks(newRows, newCols);
237       assign_from_sparse(rowind, colind, values,
238 			 rowPermutation, colPermutation);
239     }
240     /**< Same as above, except taking sizes and blocks arguments.
241      */
242 
243 
244     /** Add given set of values to the matrix.
245      *  The values should be given in upper triangular storage.
246      */
add_values(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> const & values)247     inline void add_values
248       (std::vector<int> const & rowind,
249        std::vector<int> const & colind,
250        std::vector<Treal> const & values) {
251       this->matrixPtr->syAddValues(rowind, colind, values);
252     }
253 
254     /** Same as above, except taking two additional arguments
255      *  specifying the permutation of rows and columns.
256      */
add_values(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> const & values,std::vector<int> const & rowPermutation,std::vector<int> const & colPermutation)257     inline void add_values
258       (std::vector<int> const & rowind,
259        std::vector<int> const & colind,
260        std::vector<Treal> const & values,
261        std::vector<int> const & rowPermutation,
262        std::vector<int> const & colPermutation) {
263       std::vector<int> newRowind;
264       std::vector<int> newColind;
265       this->getPermutedAndSymmetrized(rowind, rowPermutation, newRowind,
266 				      colind, colPermutation, newColind);
267       this->matrixPtr->syAddValues(newRowind, newColind, values);
268     }
269 
270 
271 
get_values(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> & values)272     inline void get_values
273       (std::vector<int> const & rowind,
274        std::vector<int> const & colind,
275        std::vector<Treal> & values) const {
276       this->matrixPtr->syGetValues(rowind, colind, values);
277     }
278     /**< Get values given by row and column index lists.
279      * Input arrays contain row and column indices.
280      * The wanted elements must be given in upper triangluar storage.
281      * The output array contains values for the given indices.
282      * @warning All indexing start at zero.
283      */
284 
get_values(std::vector<int> const & rowind,std::vector<int> const & colind,std::vector<Treal> & values,std::vector<int> const & rowPermutation,std::vector<int> const & colPermutation)285     inline void get_values
286       (std::vector<int> const & rowind,
287        std::vector<int> const & colind,
288        std::vector<Treal> & values,
289        std::vector<int> const & rowPermutation,
290        std::vector<int> const & colPermutation) const {
291       std::vector<int> newRowind;
292       std::vector<int> newColind;
293       this->getPermutedAndSymmetrized(rowind, rowPermutation, newRowind,
294 				      colind, colPermutation, newColind);
295       this->matrixPtr->syGetValues(newRowind, newColind, values);
296     }
297     /**< Same as above, except taking two additional arguments
298      *   specifying the permutation of rows and columns.
299      */
300 
301 
get_all_values(std::vector<int> & rowind,std::vector<int> & colind,std::vector<Treal> & values)302     inline void get_all_values
303       (std::vector<int> & rowind,
304        std::vector<int> & colind,
305        std::vector<Treal> & values) const {
306       rowind.resize(0);
307       colind.resize(0);
308       values.resize(0);
309       rowind.reserve(nnz());
310       colind.reserve(nnz());
311       values.reserve(nnz());
312       this->matrixPtr->syGetAllValues(rowind, colind, values);
313     }
314     /**< Get all values and corresponding row and column index lists,
315      * in matrix. Only upper triangle values are returned.
316      * @warning All indexing start at zero.
317      */
318 
get_all_values(std::vector<int> & rowind,std::vector<int> & colind,std::vector<Treal> & values,std::vector<int> const & rowInversePermutation,std::vector<int> const & colInversePermutation)319     inline void get_all_values
320       (std::vector<int> & rowind,
321        std::vector<int> & colind,
322        std::vector<Treal> & values,
323        std::vector<int> const & rowInversePermutation,
324        std::vector<int> const & colInversePermutation) const {
325       std::vector<int> tmpRowind;
326       std::vector<int> tmpColind;
327       tmpRowind.reserve(rowind.capacity());
328       tmpColind.reserve(colind.capacity());
329       values.resize(0);
330       this->matrixPtr->syGetAllValues(tmpRowind, tmpColind, values);
331       this->getPermutedAndSymmetrized(tmpRowind, rowInversePermutation, rowind,
332 				      tmpColind, colInversePermutation, colind);
333     }
334     /**< Same as above, except taking two additional arguments
335      *   specifying the permutation of rows and columns.
336      *   Note, however, that this permutation is the inverse
337      *   permutation compared to the permutations provided in the
338      *   functions "assign_from_sparse", "add_values", and "get_values"
339      *   @warning permutation is inverse compared to other functions
340      */
341 
342 
343 
344     MatrixSymmetric<Treal, Tmatrix>&
345       operator=(const MatrixSymmetric<Treal, Tmatrix>& symm) {
346       MatrixBase<Treal, Tmatrix>::operator=(symm);
347       return *this;
348     }
349     MatrixSymmetric<Treal, Tmatrix>&
350       operator=(const MatrixGeneral<Treal, Tmatrix>& matr) {
351       MatrixBase<Treal, Tmatrix>::operator=(matr);
352       this->matrixPtr->nosymToSym();
353       return *this;
354     }
355     inline MatrixSymmetric<Treal, Tmatrix>& operator=(int const k) {
356       *this->matrixPtr = k;
357       return *this;
358     }
359 
frob()360     inline Treal frob() const {
361       return this->matrixPtr->syFrob();
362     }
363     Treal mixed_norm(Treal const requestedAccuracy,
364 		     int maxIter = -1) const;
365     Treal eucl(Treal const requestedAccuracy,
366 	       int maxIter = -1) const;
367 
quickEuclBounds(Treal & euclLowerBound,Treal & euclUpperBound)368     void quickEuclBounds(Treal & euclLowerBound,
369 			 Treal & euclUpperBound) const {
370       Treal frobTmp = frob();
371       euclLowerBound = frobTmp  / template_blas_sqrt( (Treal)this->get_nrows() );
372       euclUpperBound = frobTmp;
373     }
374 
375 
376     /** Returns interval containing the Euclidean norm of A - B
377      * ( || A - B ||_2 )
378      * @see eucl_diff
379      * @see frob_diff
380      */
381       static Interval<Treal>
382 	diff(const MatrixSymmetric<Treal, Tmatrix>& A,
383 	     const MatrixSymmetric<Treal, Tmatrix>& B,
384 	     normType const norm,
385 	     Treal const requestedAccuracy);
386     /** Returns interval containing the Euclidean norm of A - B
387      *  ( || A - B ||_2 ) based on the chosen norm.
388      *  BUT, in the case of Euclidean norm, the norm is only computed with
389      *  the requested accuracy if it is smaller than 'maxAbsVal'.
390      *  @see euclDiffIfSmall
391      *  @see frob_diff
392      */
393       static Interval<Treal>
394       diffIfSmall(const MatrixSymmetric<Treal, Tmatrix>& A,
395 		  const MatrixSymmetric<Treal, Tmatrix>& B,
396 		  normType const norm,
397 		  Treal const requestedAccuracy,
398 		  Treal const maxAbsVal);
399     /** Returns the Frobenius norm of A - B
400      *  ( || A - B ||_F )
401      */
frob_diff(const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B)402     static inline Treal frob_diff
403       (const MatrixSymmetric<Treal, Tmatrix>& A,
404        const MatrixSymmetric<Treal, Tmatrix>& B) {
405       return Tmatrix::syFrobDiff(*A.matrixPtr, *B.matrixPtr);
406     }
407 
408     /** Returns the Euclidean norm of A - B
409      *  ( || A - B ||_2 )
410      */
411     static Treal eucl_diff
412       (const MatrixSymmetric<Treal, Tmatrix>& A,
413        const MatrixSymmetric<Treal, Tmatrix>& B,
414        Treal const requestedAccuracy,
415        int maxIter = -1);
416 
417     /** Returns the 'mixed' norm of A - B
418      *  ( || A - B ||_mixed )
419      */
420     static Treal mixed_diff
421       (const MatrixSymmetric<Treal, Tmatrix>& A,
422        const MatrixSymmetric<Treal, Tmatrix>& B,
423        Treal const requestedAccuracy);
424 
425     /** Returns interval containing the Euclidean norm of A - B
426      *  ( || A - B ||_2 ).
427      *  BUT, the norm is only computed with
428      *  the requested accuracy if it is smaller than 'maxAbsVal'.
429      *  Otherwise, the Frobenius norm is used to get the bounds.
430      */
431     static Interval<Treal> euclDiffIfSmall
432       (const MatrixSymmetric<Treal, Tmatrix>& A,
433        const MatrixSymmetric<Treal, Tmatrix>& B,
434        Treal const requestedAccuracy,
435        Treal const maxAbsVal,
436        VectorType * const eVecPtr = 0);
437 
438 
439     /** Does thresholding so that the error in the chosen norm is below
440      *  the given threshold. Returns the actual introduced error.
441      *  In case of the Frobenius norm the return value may be an upper bound.
442      *  In case of the Euclidean norm the return value is sometimes an
443      *  upper bound as well but it can only happen if the whole matrix
444      *  is removed.
445      *
446      *  @see frob_thresh(Treal)
447      *  @see eucl_thresh(Treal const)
448      */
449     Treal thresh(Treal const threshold,
450 		 normType const norm);
451 
452     /** Does thresholding so that the error in the Frobenius norm
453      *  is below the given threshold.
454      *  Returns an upper bound of the introduced error.
455      *  If no elements on the block diagonal are removed the return value
456      *  is equal to the introduced error.
457      */
frob_thresh(Treal const threshold)458     inline Treal frob_thresh(Treal const threshold) {
459       return 2.0 * this->matrixPtr->frob_thresh(threshold / 2);
460     }
461 
462     Treal eucl_thresh(Treal const threshold,
463 		      MatrixTriangular<Treal, Tmatrix> const * const Zptr = NULL);
464 
465     Treal eucl_element_level_thresh(Treal const threshold);
466 
467     void getSizesAndBlocksForFrobNormMat
468       ( SizesAndBlocks & rows_new, SizesAndBlocks & cols_new ) const;
469 
470     Treal mixed_norm_thresh(Treal const threshold);
471 
simple_blockwise_frob_thresh(Treal const threshold)472     void simple_blockwise_frob_thresh(Treal const threshold) {
473       this->matrixPtr->frobThreshLowestLevel(threshold*threshold, 0);
474     }
475 
gershgorin(Treal & lmin,Treal & lmax)476     inline void gershgorin(Treal& lmin, Treal& lmax) const {
477       this->matrixPtr->sy_gershgorin(lmin, lmax);
478     }
trace_ab(const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B)479     static inline Treal trace_ab
480       (const MatrixSymmetric<Treal, Tmatrix>& A,
481        const MatrixSymmetric<Treal, Tmatrix>& B) {
482       return Tmatrix::sy_trace_ab(*A.matrixPtr, *B.matrixPtr);
483     }
nnz()484     inline size_t nnz() const { /* Note: size_t instead of int here to avoid integer overflow. */
485       return this->matrixPtr->sy_nnz();
486     }
nvalues()487     inline size_t nvalues() const { /* Note: size_t instead of int here to avoid integer overflow. */
488       return this->matrixPtr->sy_nvalues();
489     }
write_to_buffer(void * buffer,const int n_bytes)490     inline void write_to_buffer(void* buffer, const int n_bytes) const {
491       this->write_to_buffer_base(buffer, n_bytes, matrix_symm);
492     }
read_from_buffer(void * buffer,const int n_bytes)493     inline void read_from_buffer(void* buffer, const int n_bytes) {
494       this->read_from_buffer_base(buffer, n_bytes, matrix_symm);
495     }
496 
497 
498     /** B = alpha * A   : A and B are symmetric*/
499     MatrixSymmetric<Treal, Tmatrix>& operator=
500       (XY<Treal, MatrixSymmetric<Treal, Tmatrix> > const & sm);
501     /** C = alpha * A * A + beta * C          : A and C are symmetric    */
502     MatrixSymmetric<Treal, Tmatrix>& operator=
503       (const XYZpUV<Treal,
504        MatrixSymmetric<Treal, Tmatrix>,
505        MatrixSymmetric<Treal, Tmatrix>,
506        Treal,
507        MatrixSymmetric<Treal, Tmatrix> >& sm2psm);
508     /** C = alpha * A * A                     : A and C are symmetric    */
509     MatrixSymmetric<Treal, Tmatrix>& operator=
510       (const XYZ<Treal,
511        MatrixSymmetric<Treal, Tmatrix>,
512        MatrixSymmetric<Treal, Tmatrix> >& sm2);
513     /** C += alpha * A * A                    : A and C are symmetric    */
514     MatrixSymmetric<Treal, Tmatrix>& operator+=
515       (const XYZ<Treal,
516        MatrixSymmetric<Treal, Tmatrix>,
517        MatrixSymmetric<Treal, Tmatrix> >& sm2);
518     /** C = alpha * A * transpose(A) + beta * C        : C is symmetric  */
519     MatrixSymmetric<Treal, Tmatrix>& operator=
520       (const XYZpUV<Treal,
521        MatrixGeneral<Treal, Tmatrix>,
522        MatrixGeneral<Treal, Tmatrix>,
523        Treal,
524        MatrixSymmetric<Treal, Tmatrix> >& smmpsm);
525     /** C = alpha * A * transpose(A)                   : C is symmetric  */
526     MatrixSymmetric<Treal, Tmatrix>& operator=
527       (const XYZ<Treal,
528        MatrixGeneral<Treal, Tmatrix>,
529        MatrixGeneral<Treal, Tmatrix> >& smm);
530     /** C += alpha * A * transpose(A)                   : C is symmetric  */
531     MatrixSymmetric<Treal, Tmatrix>& operator+=
532       (const XYZ<Treal,
533        MatrixGeneral<Treal, Tmatrix>,
534        MatrixGeneral<Treal, Tmatrix> >& smm);
535 
536     /** A = Z * A * transpose(Z) : Z is upper triangular and A is symmetric;
537      *  A = transpose(Z) * A * Z : Z is upper triangular and A is symmetric
538      */
539     MatrixSymmetric<Treal, Tmatrix>& operator=
540       (const XYZ<MatrixTriangular<Treal, Tmatrix>,
541        MatrixSymmetric<Treal, Tmatrix>,
542        MatrixTriangular<Treal, Tmatrix> >& zaz);
543 
544     /** C = alpha * A * B + beta * C where A and B are symmetric
545      *  and only the upper triangle of C is computed,
546      *  C is enforced to be symmetric!
547      */
548     static void ssmmUpperTriangleOnly(const Treal alpha,
549 				      const MatrixSymmetric<Treal, Tmatrix>& A,
550 				      const MatrixSymmetric<Treal, Tmatrix>& B,
551 				      const Treal beta,
552 				      MatrixSymmetric<Treal, Tmatrix>& C);
553 
554 
555     /* Addition */
556     /** C =  A + B       : A, B, and C are symmetric  */
557     MatrixSymmetric<Treal, Tmatrix>& operator=
558       (XpY<MatrixSymmetric<Treal, Tmatrix>,
559        MatrixSymmetric<Treal, Tmatrix> > const & mpm);
560     /** C =  A - B       : A, B, and C are symmetric  */
561     MatrixSymmetric<Treal, Tmatrix>& operator=
562       (XmY<MatrixSymmetric<Treal, Tmatrix>,
563        MatrixSymmetric<Treal, Tmatrix> > const & mm);
564     /** B += A           : A and B are symmetric */
565     MatrixSymmetric<Treal, Tmatrix>& operator+=
566       (MatrixSymmetric<Treal, Tmatrix> const & A);
567     /** B -= A           : A and B are symmetric */
568     MatrixSymmetric<Treal, Tmatrix>& operator-=
569       (MatrixSymmetric<Treal, Tmatrix> const & A);
570 
571     /** B += alpha * A   : A and B are symmetric*/
572     MatrixSymmetric<Treal, Tmatrix>& operator+=
573       (XY<Treal, MatrixSymmetric<Treal, Tmatrix> > const & sm);
574 
575     /** B -= alpha * A   : A and B are symmetric*/
576     MatrixSymmetric<Treal, Tmatrix>& operator-=
577       (XY<Treal, MatrixSymmetric<Treal, Tmatrix> > const & sm);
578 
579     template<typename Top>
accumulateWith(Top & op)580       Treal accumulateWith(Top & op) {
581       return this->matrixPtr->syAccumulateWith(op);
582     }
583 
random()584     void random() {
585       this->matrixPtr->syRandom();
586     }
587 
randomZeroStructure(Treal probabilityBeingZero)588     void randomZeroStructure(Treal probabilityBeingZero) {
589       this->matrixPtr->syRandomZeroStructure(probabilityBeingZero);
590     }
591 
592     /** Uses rule depending on the row and column indexes to set matrix elements
593      * The Trule class should have the function "Treal = set(int row,int col)"
594      * which is used to set the elements.
595      */
596     template<typename TRule>
setElementsByRule(TRule & rule)597       void setElementsByRule(TRule & rule) {
598       this->matrixPtr->sySetElementsByRule(rule);
599       return;
600     }
601 
602     /** Transfer this matrix to dest, clearing previous content of
603 	dest if any. */
transfer(MatrixSymmetric<Treal,Tmatrix> & dest)604     void transfer( MatrixSymmetric<Treal, Tmatrix> & dest ) {
605       ValidPtr<Tmatrix>::swap( this->matrixPtr, dest.matrixPtr );
606       // *this now contains previous content of dest
607       this->clear();
608     }
609 
610     template<typename Tvector>
matVecProd(Tvector & y,Tvector const & x)611       void matVecProd(Tvector & y, Tvector const & x) const {
612       Treal const ONE = 1.0;
613       y = (ONE * (*this) * x);
614     }
615 
616 
obj_type_id()617     std::string obj_type_id() const {return "MatrixSymmetric";}
618   protected:
writeToFileProt(std::ofstream & file)619     inline void writeToFileProt(std::ofstream & file) const {
620       this->writeToFileBase(file, matrix_symm);
621     }
readFromFileProt(std::ifstream & file)622     inline void readFromFileProt(std::ifstream & file) {
623       this->readFromFileBase(file, matrix_symm);
624     }
625 
626     /** This function permutes row and column indices according to the
627      *  specified permutation and gives the indices as upper triangle
628      *  in the new permutation.
629      *  @warning Duplicate indices are kept.
630      */
getPermutedAndSymmetrized(std::vector<int> const & rowind,std::vector<int> const & rowPermutation,std::vector<int> & newRowind,std::vector<int> const & colind,std::vector<int> const & colPermutation,std::vector<int> & newColind)631     static void getPermutedAndSymmetrized
632       (std::vector<int> const & rowind,
633        std::vector<int> const & rowPermutation,
634        std::vector<int> & newRowind,
635        std::vector<int> const & colind,
636        std::vector<int> const & colPermutation,
637        std::vector<int> & newColind) {
638       MatrixBase<Treal, Tmatrix>::
639 	getPermutedIndexes(rowind, rowPermutation, newRowind);
640       MatrixBase<Treal, Tmatrix>::
641 	getPermutedIndexes(colind, colPermutation, newColind);
642       int tmp;
643       for (unsigned int i = 0; i < newRowind.size(); ++i) {
644 	if (newRowind[i] > newColind[i]) {
645 	  tmp = newRowind[i];
646 	  newRowind[i] = newColind[i];
647 	  newColind[i] = tmp;
648 	}
649       }
650     }
651   private:
652   };
653 
654   template<typename Treal, typename Tmatrix>
655     Treal MatrixSymmetric<Treal, Tmatrix>::
mixed_norm(Treal const requestedAccuracy,int maxIter)656     mixed_norm(Treal const requestedAccuracy,
657 	       int maxIter) const {
658     // Construct SizesAndBlocks for frobNormMat
659     SizesAndBlocks rows_new;
660     SizesAndBlocks cols_new;
661     this->getSizesAndBlocksForFrobNormMat( rows_new, cols_new );
662     // Now we can construct an empty matrix where the Frobenius norms
663     // of lowest level nonzero submatrices will be stored
664     MatrixSymmetric<Treal, typename Tmatrix::ElementType> frobNormMat;
665     frobNormMat.resetSizesAndBlocks(rows_new, cols_new);
666     frobNormMat.getMatrix().syAssignFrobNormsLowestLevel(this->getMatrix());
667     return frobNormMat.eucl(requestedAccuracy, maxIter);
668   }
669 
670 
671   template<typename Treal, typename Tmatrix>
672     Treal MatrixSymmetric<Treal, Tmatrix>::
eucl(Treal const requestedAccuracy,int maxIter)673     eucl(Treal const requestedAccuracy,
674 	 int maxIter) const {
675     assert(requestedAccuracy >= 0);
676     /* Check if norm is really small, in that case quick return */
677     Treal frobTmp = this->frob();
678     if (frobTmp < requestedAccuracy)
679       return (Treal)0.0;
680     if (maxIter < 0)
681       maxIter = this->get_nrows() * 100;
682     VectorType guess;
683     SizesAndBlocks cols;
684     this->getCols(cols);
685     guess.resetSizesAndBlocks(cols);
686     guess.rand();
687     // Elias note 2010-03-26: changed this back from "new code" to "old code" to reduce memory usage.
688 #if 0 // "new code"
689     MatrixSymmetric<Treal, Tmatrix> Copy(*this);
690     Copy.frob_thresh(requestedAccuracy / 2.0);
691     arn::LanczosLargestMagnitudeEig
692       <Treal, MatrixSymmetric<Treal, Tmatrix>, VectorType>
693       lan(Copy, guess, maxIter);
694     lan.setAbsTol( requestedAccuracy / 2.0 );
695 #else // "old code"
696     arn::LanczosLargestMagnitudeEig
697       <Treal, MatrixSymmetric<Treal, Tmatrix>, VectorType>
698       lan(*this, guess, maxIter);
699     lan.setAbsTol( requestedAccuracy );
700 #endif
701     lan.run();
702     Treal eVal = 0;
703     Treal acc = 0;
704     lan.getLargestMagnitudeEig(eVal, acc);
705     return template_blas_fabs(eVal);
706   }
707 
708   template<typename Treal, typename Tmatrix>
709     Interval<Treal> MatrixSymmetric<Treal, Tmatrix>::
diff(const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B,normType const norm,Treal const requestedAccuracy)710     diff(const MatrixSymmetric<Treal, Tmatrix>& A,
711 	 const MatrixSymmetric<Treal, Tmatrix>& B,
712 	 normType const norm, Treal const requestedAccuracy) {
713     Treal diff;
714     Treal eNMin;
715     switch (norm) {
716     case frobNorm:
717       diff = frob_diff(A, B);
718       return Interval<Treal>(diff / template_blas_sqrt((Treal)A.get_nrows()), diff);
719       break;
720     case euclNorm:
721       diff = eucl_diff(A, B, requestedAccuracy);
722       eNMin = diff - requestedAccuracy;
723       eNMin = eNMin >= 0 ? eNMin : 0;
724       return Interval<Treal>(eNMin, diff + requestedAccuracy);
725       break;
726     default:
727       throw Failure("MatrixSymmetric<Treal, Tmatrix>::"
728 		    "diff(const MatrixSymmetric<Treal, Tmatrix>&, "
729 		    "const MatrixSymmetric<Treal, Tmatrix>&, "
730 		    "normType const, Treal): "
731 		    "Diff not implemented for selected norm");
732     }
733   }
734 
735 #if 1
736   template<typename Treal, typename Tmatrix>
737     Interval<Treal> MatrixSymmetric<Treal, Tmatrix>::
diffIfSmall(const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B,normType const norm,Treal const requestedAccuracy,Treal const maxAbsVal)738     diffIfSmall(const MatrixSymmetric<Treal, Tmatrix>& A,
739 		const MatrixSymmetric<Treal, Tmatrix>& B,
740 		normType const norm,
741 		Treal const requestedAccuracy,
742 		Treal const maxAbsVal) {
743     Treal diff;
744     switch (norm) {
745     case frobNorm:
746       {
747 	diff = frob_diff(A, B);
748 	return Interval<Treal>(diff / template_blas_sqrt((Treal)A.get_nrows()), diff);
749       }
750       break;
751     case euclNorm:
752       return euclDiffIfSmall(A, B, requestedAccuracy, maxAbsVal);
753       break;
754     default:
755       throw Failure("MatrixSymmetric<Treal, Tmatrix>::"
756 		    "diffIfSmall"
757 		    "(const MatrixSymmetric<Treal, Tmatrix>&, "
758 		    "const MatrixSymmetric<Treal, Tmatrix>&, "
759 		    "normType const, Treal const, Treal const): "
760 		    "Diff not implemented for selected norm");
761     }
762   }
763 
764 #endif
765 
766 
767   template<typename Treal, typename Tmatrix>
eucl_diff(const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B,Treal const requestedAccuracy,int maxIter)768     Treal MatrixSymmetric<Treal, Tmatrix>::eucl_diff
769     (const MatrixSymmetric<Treal, Tmatrix>& A,
770      const MatrixSymmetric<Treal, Tmatrix>& B,
771      Treal const requestedAccuracy,
772      int maxIter) {
773     // DiffMatrix is a lightweight proxy object:
774     mat::DiffMatrix< MatrixSymmetric<Treal, Tmatrix>, Treal> Diff(A, B);
775     Treal maxAbsVal = 2 * frob_diff(A,B);
776     // Now, maxAbsVal should be larger than the Eucl norm
777     // Note that mat::euclIfSmall lies outside this class
778     Treal relTol = template_blas_sqrt(template_blas_sqrt(mat::getMachineEpsilon<Treal>()));
779     VectorType * const eVecPtrNotUsed = 0;
780     Interval<Treal> euclInt =
781       mat::euclIfSmall(Diff, requestedAccuracy, relTol, maxAbsVal, eVecPtrNotUsed, maxIter);
782     return euclInt.midPoint();
783   }
784 
785   template<typename Treal, typename Tmatrix>
mixed_diff(const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B,Treal const requestedAccuracy)786     Treal MatrixSymmetric<Treal, Tmatrix>::mixed_diff
787     (const MatrixSymmetric<Treal, Tmatrix>& A,
788      const MatrixSymmetric<Treal, Tmatrix>& B,
789      Treal const requestedAccuracy) {
790     MatrixSymmetric<Treal, typename Tmatrix::ElementType> frobNormMat;
791     {
792       SizesAndBlocks rows_new;
793       SizesAndBlocks cols_new;
794       A.getSizesAndBlocksForFrobNormMat( rows_new, cols_new );
795       frobNormMat.resetSizesAndBlocks(rows_new, cols_new);
796       frobNormMat.getMatrix().syAssignDiffFrobNormsLowestLevel(A.getMatrix(),B.getMatrix());
797     }
798     return frobNormMat.eucl(requestedAccuracy);
799   }
800 
801 #if 1
802   template<typename Treal, typename Tmatrix>
euclDiffIfSmall(const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B,Treal const requestedAccuracy,Treal const maxAbsVal,VectorType * const eVecPtr)803     Interval<Treal> MatrixSymmetric<Treal, Tmatrix>::euclDiffIfSmall
804     (const MatrixSymmetric<Treal, Tmatrix>& A,
805      const MatrixSymmetric<Treal, Tmatrix>& B,
806      Treal const requestedAccuracy,
807      Treal const maxAbsVal,
808      VectorType * const eVecPtr) {
809     // DiffMatrix is a lightweight proxy object:
810     mat::DiffMatrix< MatrixSymmetric<Treal, Tmatrix>, Treal> Diff(A, B);
811     // Note that this function lies outside this class
812     Treal relTol = template_blas_sqrt(template_blas_sqrt(mat::getMachineEpsilon<Treal>()));
813     Interval<Treal> tmpInterval = mat::euclIfSmall(Diff, requestedAccuracy, relTol, maxAbsVal, eVecPtr);
814     // Emanuel note: Ugly fix to make certain tests pass, we expand
815     // the interval up to the requested accuracy. Note that larger
816     // intervals may occur if the norm is not 'small'. It happens that
817     // Lanczos misconverges to for example the second largest
818     // eigenvalue. This happens in particular when the first and second
819     // eigenvalues are very close (of the order of the requested
820     // accuracy). Expanding the interval makes the largest eigenvalue
821     // (at least for certain cases) end up inside the interval even
822     // though Lanczos has misconverged.
823     if ( tmpInterval.length() < 2*requestedAccuracy )
824       return Interval<Treal>( tmpInterval.midPoint()-requestedAccuracy, tmpInterval.midPoint()+requestedAccuracy );
825     return tmpInterval;
826   }
827 
828 #endif
829 
830 
831   template<typename Treal, typename Tmatrix>
832     Treal MatrixSymmetric<Treal, Tmatrix>::
thresh(Treal const threshold,normType const norm)833     thresh(Treal const threshold,
834 	   normType const norm) {
835     switch (norm) {
836     case frobNorm:
837       return this->frob_thresh(threshold);
838       break;
839     case euclNorm:
840       return this->eucl_thresh(threshold);
841       break;
842     case mixedNorm:
843       return this->mixed_norm_thresh(threshold);
844       break;
845     default:
846       throw Failure("MatrixSymmetric<Treal, Tmatrix>::"
847 		    "thresh(Treal const, "
848 		    "normType const): "
849 		    "Thresholding not implemented for selected norm");
850     }
851   }
852 
853 #if 1
854 
855   template<typename Treal, typename Tmatrix>
856     Treal MatrixSymmetric<Treal, Tmatrix>::
eucl_thresh(Treal const threshold,MatrixTriangular<Treal,Tmatrix> const * const Zptr)857     eucl_thresh(Treal const threshold,
858 		MatrixTriangular<Treal, Tmatrix> const * const Zptr) {
859     if ( Zptr == NULL ) {
860       EuclTruncationSymm<MatrixSymmetric<Treal, Tmatrix>, Treal> TruncObj(*this);
861       return TruncObj.run( threshold );
862     }
863     EuclTruncationSymmWithZ<MatrixSymmetric<Treal, Tmatrix>, MatrixTriangular<Treal, Tmatrix>, Treal> TruncObj(*this, *Zptr);
864     return TruncObj.run( threshold );
865   }
866 
867 #endif
868 
869 
870   template<typename Treal, typename Tmatrix>
getSizesAndBlocksForFrobNormMat(SizesAndBlocks & rows_new,SizesAndBlocks & cols_new)871     void MatrixSymmetric<Treal, Tmatrix>::getSizesAndBlocksForFrobNormMat
872     ( SizesAndBlocks & rows_new, SizesAndBlocks & cols_new ) const {
873     std::vector<int> rows_block_sizes;
874     std::vector<int> cols_block_sizes;
875     int n_rows;
876     int n_cols;
877     {
878       SizesAndBlocks rows;
879       SizesAndBlocks cols;
880       this->getRows(rows);
881       this->getCols(cols);
882       rows.getBlockSizeVector( rows_block_sizes );
883       cols.getBlockSizeVector( cols_block_sizes );
884       rows_block_sizes.pop_back(); // Remove the '1' at the end
885       cols_block_sizes.pop_back(); // Remove the '1' at the end
886       n_rows = rows.getNTotalScalars();
887       n_cols = cols.getNTotalScalars();
888       int factor_rows = rows_block_sizes[rows_block_sizes.size()-1];
889       int factor_cols = cols_block_sizes[cols_block_sizes.size()-1];
890       for (unsigned int ind = 0; ind < rows_block_sizes.size(); ++ind)
891 	rows_block_sizes[ind] = rows_block_sizes[ind] / factor_rows;
892       for (unsigned int ind = 0; ind < cols_block_sizes.size(); ++ind)
893 	cols_block_sizes[ind] = cols_block_sizes[ind] / factor_cols;
894       // Now set the number of (scalar) rows and cols, should be equal
895       // to the number of blocks at the lowest level of the original
896       // matrix
897       if (n_rows % factor_rows)
898 	n_rows = n_rows / factor_rows + 1;
899       else
900 	n_rows = n_rows / factor_rows;
901       if (n_cols % factor_cols)
902 	n_cols = n_cols / factor_cols + 1;
903       else
904 	n_cols = n_cols / factor_cols;
905     }
906     rows_new = SizesAndBlocks( rows_block_sizes, n_rows );
907     cols_new = SizesAndBlocks( cols_block_sizes, n_cols );
908   }
909 
910 
911   template<typename Treal, typename Tmatrix>
912     Treal MatrixSymmetric<Treal, Tmatrix>::
mixed_norm_thresh(Treal const threshold)913     mixed_norm_thresh(Treal const threshold) {
914     assert(threshold >= (Treal)0.0);
915     if (threshold == (Treal)0.0)
916       return (Treal)0;
917     // Construct SizesAndBlocks for frobNormMat
918     SizesAndBlocks rows_new;
919     SizesAndBlocks cols_new;
920     this->getSizesAndBlocksForFrobNormMat( rows_new, cols_new );
921     // Now we can construct an empty matrix where the Frobenius norms
922     // of lowest level nonzero submatrices will be stored
923     MatrixSymmetric<Treal, typename Tmatrix::ElementType> frobNormMat;
924     frobNormMat.resetSizesAndBlocks(rows_new, cols_new);
925     // We want the following step to dominate the mixed_norm truncation (this
926     // is where Frobenius norms of submatrices are computed, i.e. it
927     // is here we loop over all matrix elements.)
928     frobNormMat.getMatrix().syAssignFrobNormsLowestLevel(this->getMatrix());
929     EuclTruncationSymmElementLevel<MatrixSymmetric<Treal, typename Tmatrix::ElementType>, Treal> TruncObj( frobNormMat );
930     Treal mixed_norm_result = TruncObj.run( threshold );
931     frobNormMat.getMatrix().truncateAccordingToSparsityPattern(this->getMatrix());
932     return mixed_norm_result;
933   }
934 
935 
936   /* B = alpha * A */
937   template<typename Treal, typename Tmatrix>
938     inline MatrixSymmetric<Treal, Tmatrix>&
939     MatrixSymmetric<Treal, Tmatrix>::operator=
940     (XY<Treal, MatrixSymmetric<Treal, Tmatrix> > const & sm) {
941 
942   if(this == &sm.B) // A = B
943       {
944         *this *= sm.A; // B *= alpha
945         return *this;
946       }
947     assert(!sm.tB);
948     /* Data structure set by assign - therefore set haveDataStructure to true */
949     this->matrixPtr.haveDataStructureSet(true);
950     this->matrixPtr->assign(sm.A, *sm.B.matrixPtr);
951     return *this;
952   }
953   /* C = alpha * A * A + beta * C          : A and C are symmetric    */
954   template<typename Treal, typename Tmatrix>
955     MatrixSymmetric<Treal, Tmatrix>&
956     MatrixSymmetric<Treal, Tmatrix>::operator=
957     (const XYZpUV<Treal,
958      MatrixSymmetric<Treal, Tmatrix>,
959      MatrixSymmetric<Treal, Tmatrix>,
960      Treal,
961      MatrixSymmetric<Treal, Tmatrix> >& sm2psm) {
962     assert(this != &sm2psm.B);
963     if (this == &sm2psm.E && &sm2psm.B == &sm2psm.C) {
964       /* Operation is C = alpha * A * A + beta * C */
965       Tmatrix::sysq('U',
966 		    sm2psm.A, *sm2psm.B.matrixPtr,
967 		    sm2psm.D, *this->matrixPtr);
968       return *this;
969     }
970     else /* this != &sm2psm.C || &sm2psm.B != &sm2psm.C */
971       throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
972 		    "(const XYZpUV<Treal, MatrixSymmetric"
973 		    "<Treal, Tmatrix> >& sm2psm) :  "
974 		    "D = alpha * A * B + beta * C not supported for C != D"
975 		    " and for symmetric matrices not for A != B since this "
976 		    "generally will result in a nonsymmetric matrix");
977   }
978 
979   /* C = alpha * A * A                     : A and C are symmetric    */
980   template<typename Treal, typename Tmatrix>
981     MatrixSymmetric<Treal, Tmatrix>&
982     MatrixSymmetric<Treal, Tmatrix>::operator=
983     (const XYZ<Treal,
984      MatrixSymmetric<Treal, Tmatrix>,
985      MatrixSymmetric<Treal, Tmatrix> >& sm2) {
986     assert(this != &sm2.B);
987     if (&sm2.B == &sm2.C) {
988       this->matrixPtr.haveDataStructureSet(true);
989       Tmatrix::sysq('U', sm2.A, *sm2.B.matrixPtr, 0, *this->matrixPtr);
990       return *this;
991     }
992     else
993       throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
994 		    "(const XYZ<Treal, MatrixSymmetric<Treal, Tmatrix>,"
995 		    " MatrixSymmetric<Treal, Tmatrix> >& sm2) :  "
996 		    "Operation C = alpha * A * B with only symmetric "
997 		    "matrices not supported for A != B");
998   }
999 
1000   /* C += alpha * A * A                    : A and C are symmetric    */
1001   template<typename Treal, typename Tmatrix>
1002     MatrixSymmetric<Treal, Tmatrix>&
1003     MatrixSymmetric<Treal, Tmatrix>::operator+=
1004     (const XYZ<Treal,
1005      MatrixSymmetric<Treal, Tmatrix>,
1006      MatrixSymmetric<Treal, Tmatrix> >& sm2) {
1007     assert(this != &sm2.B);
1008     if (&sm2.B == &sm2.C) {
1009       Tmatrix::sysq('U', sm2.A, *sm2.B.matrixPtr, 1, *this->matrixPtr);
1010       return *this;
1011     }
1012     else
1013       throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator+="
1014 		    "(const XYZ<Treal, MatrixSymmetric<Treal, Tmatrix>,"
1015 		    " MatrixSymmetric<Treal, Tmatrix> >& sm2) :  "
1016 		    "Operation C += alpha * A * B with only symmetric "
1017 		    "matrices not supported for A != B");
1018   }
1019 
1020   /* C = alpha * A * transpose(A) + beta * C        : C is symmetric  */
1021   template<typename Treal, typename Tmatrix>
1022     MatrixSymmetric<Treal, Tmatrix>&
1023     MatrixSymmetric<Treal, Tmatrix>::operator=
1024     (const XYZpUV<Treal,
1025      MatrixGeneral<Treal, Tmatrix>,
1026      MatrixGeneral<Treal, Tmatrix>,
1027      Treal,
1028      MatrixSymmetric<Treal, Tmatrix> >& smmpsm) {
1029     if (this == &smmpsm.E)
1030       if (&smmpsm.B == &smmpsm.C)
1031 	if (!smmpsm.tB && smmpsm.tC) {
1032 	  Tmatrix::syrk('U', false,
1033 			smmpsm.A, *smmpsm.B.matrixPtr,
1034 			smmpsm.D, *this->matrixPtr);
1035 	}
1036 	else
1037 	  throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1038 			"(const XYZpUV<Treal, MatrixGeneral"
1039 			"<Treal, Tmatrix>, "
1040 			"MatrixGeneral<Treal, Tmatrix>, Treal, "
1041 			"MatrixSymmetric<Treal, Tmatrix> >&) : "
1042 			"C = alpha * A' * A + beta * C, not implemented"
1043 			" only C = alpha * A * A' + beta * C");
1044       else
1045 	throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1046 		      "(const XYZpUV<"
1047 		      "Treal, MatrixGeneral<Treal, Tmatrix>, "
1048 		      "MatrixGeneral<Treal, Tmatrix>, Treal, "
1049 		      "MatrixSymmetric<Treal, Tmatrix> >&) : "
1050 		      "You are trying to call C = alpha * A * A' + beta * C"
1051 		      " with  A and A' being different objects");
1052     else
1053       throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1054 		    "(const XYZpUV<"
1055 		    "Treal, MatrixGeneral<Treal, Tmatrix>, "
1056 		    "MatrixGeneral<Treal, Tmatrix>, Treal, "
1057 		    "MatrixSymmetric<Treal, Tmatrix> >&) :  "
1058 		    "D = alpha * A * A' + beta * C not supported for C != D");
1059     return *this;
1060   }
1061 
1062   /* C = alpha * A * transpose(A)                   : C is symmetric  */
1063   template<typename Treal, typename Tmatrix>
1064     MatrixSymmetric<Treal, Tmatrix>&
1065     MatrixSymmetric<Treal, Tmatrix>::operator=
1066     (const XYZ<Treal,
1067      MatrixGeneral<Treal, Tmatrix>,
1068      MatrixGeneral<Treal, Tmatrix> >& smm) {
1069     if (&smm.B == &smm.C)
1070       if (!smm.tB && smm.tC) {
1071 	Tmatrix::syrk('U', false,
1072 		      smm.A, *smm.B.matrixPtr,
1073 		      0, *this->matrixPtr);
1074       }
1075       else
1076 	throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1077 		      "(const XYZ<"
1078 		      "Treal, MatrixGeneral<Treal, Tmatrix>, "
1079 		      "MatrixGeneral<Treal, Tmatrix> >&) : "
1080 		      "C = alpha * A' * A, not implemented "
1081 		      "only C = alpha * A * A'");
1082     else
1083       throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1084 		    "(const XYZ<"
1085 		    "Treal, MatrixGeneral<Treal, Tmatrix>, "
1086 		    "MatrixGeneral<Treal, Tmatrix> >&) : "
1087 		    "You are trying to call C = alpha * A * A' "
1088 		    "with A and A' being different objects");
1089     return *this;
1090   }
1091 
1092   /* C += alpha * A * transpose(A)                   : C is symmetric  */
1093   template<typename Treal, typename Tmatrix>
1094     MatrixSymmetric<Treal, Tmatrix>&
1095     MatrixSymmetric<Treal, Tmatrix>::operator+=
1096     (const XYZ<Treal,
1097      MatrixGeneral<Treal, Tmatrix>,
1098      MatrixGeneral<Treal, Tmatrix> >& smm) {
1099     if (&smm.B == &smm.C)
1100       if (!smm.tB && smm.tC) {
1101 	Tmatrix::syrk('U', false,
1102 		      smm.A, *smm.B.matrixPtr,
1103 		      1, *this->matrixPtr);
1104       }
1105       else
1106 	throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator+="
1107 		      "(const XYZ<"
1108 		      "Treal, MatrixGeneral<Treal, Tmatrix>, "
1109 		      "MatrixGeneral<Treal, Tmatrix> >&) : "
1110 		      "C += alpha * A' * A, not implemented "
1111 		      "only C += alpha * A * A'");
1112     else
1113       throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator+="
1114 		    "(const XYZ<"
1115 		    "Treal, MatrixGeneral<Treal, Tmatrix>, "
1116 		    "MatrixGeneral<Treal, Tmatrix> >&) : "
1117 		    "You are trying to call C += alpha * A * A' "
1118 		    "with  A and A' being different objects");
1119     return *this;
1120   }
1121 
1122 #if 1
1123   /* A = op1(Z) * A * op2(Z)   : Z is upper triangular and A is symmetric */
1124   /* Either op1() or op2() is the transpose operation. */
1125   template<typename Treal, typename Tmatrix>
1126     MatrixSymmetric<Treal, Tmatrix>&
1127     MatrixSymmetric<Treal, Tmatrix>::operator=
1128     (const XYZ<MatrixTriangular<Treal, Tmatrix>,
1129      MatrixSymmetric<Treal, Tmatrix>,
1130      MatrixTriangular<Treal, Tmatrix> >& zaz) {
1131     if (this == &zaz.B) {
1132       if (&zaz.A == &zaz.C) {
1133 	if (zaz.tA && !zaz.tC) {
1134 	  Tmatrix::trsytriplemm('R', *zaz.A.matrixPtr, *this->matrixPtr);
1135 	}
1136 	else if (!zaz.tA && zaz.tC) {
1137 	  Tmatrix::trsytriplemm('L', *zaz.A.matrixPtr, *this->matrixPtr);
1138 	}
1139 	else
1140 	  throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1141 			"(const XYZ<MatrixTriangular<Treal, Tmatrix>,"
1142 			"MatrixSymmetric<Treal, Tmatrix>,"
1143 			"MatrixTriangular<Treal, Tmatrix> >&) : "
1144 			"A = op1(Z) * A * op2(Z) : Either op1 xor op2 must be "
1145 			"the transpose operation.");
1146       }
1147       else
1148 	throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1149 		      "(const XYZ<MatrixTriangular<Treal, Tmatrix>,"
1150 		      "MatrixSymmetric<Treal, Tmatrix>,"
1151 		      "MatrixTriangular<Treal, Tmatrix> >&) : "
1152 		      "A = op1(Z1) * A * op2(Z2) : Z1 and Z2 must be the same "
1153 		      "object");
1154     }
1155     else
1156       throw Failure("MatrixSymmetric<Treal, Tmatrix>::operator="
1157 		    "(const XYZ<MatrixTriangular<Treal, Tmatrix>,"
1158 		    "MatrixSymmetric<Treal, Tmatrix>,"
1159 		    "MatrixTriangular<Treal, Tmatrix> >&) : "
1160 		    "C = op1(Z) * A * op2(Z) : A and C must be the same "
1161 		    "object");
1162     return *this;
1163   }
1164 
1165 #endif
1166 
1167 
1168   /** C = alpha * A * B + beta * C where A and B are symmetric
1169    *  and only the upper triangle of C is computed,
1170    *  C is enforced to be symmetric!
1171    */
1172   template<typename Treal, typename Tmatrix>
1173     void MatrixSymmetric<Treal, Tmatrix>::
ssmmUpperTriangleOnly(const Treal alpha,const MatrixSymmetric<Treal,Tmatrix> & A,const MatrixSymmetric<Treal,Tmatrix> & B,const Treal beta,MatrixSymmetric<Treal,Tmatrix> & C)1174     ssmmUpperTriangleOnly(const Treal alpha,
1175 			  const MatrixSymmetric<Treal, Tmatrix>& A,
1176 			  const MatrixSymmetric<Treal, Tmatrix>& B,
1177 			  const Treal beta,
1178 			  MatrixSymmetric<Treal, Tmatrix>& C) {
1179     Tmatrix::ssmm_upper_tr_only(alpha, *A.matrixPtr, *B.matrixPtr,
1180 				beta, *C.matrixPtr);
1181   }
1182 
1183 
1184 
1185 
1186 
1187   /* Addition */
1188   /* C =  A + B   */
1189   template<typename Treal, typename Tmatrix>
1190     inline MatrixSymmetric<Treal, Tmatrix>&
1191     MatrixSymmetric<Treal, Tmatrix>::operator=
1192     (const XpY<MatrixSymmetric<Treal, Tmatrix>,
1193      MatrixSymmetric<Treal, Tmatrix> >& mpm) {
1194     assert(this != &mpm.A);
1195     (*this) = mpm.B;
1196     Tmatrix::add(1.0, *mpm.A.matrixPtr, *this->matrixPtr);
1197     return *this;
1198   }
1199   /* C =  A - B   */
1200   template<typename Treal, typename Tmatrix>
1201     inline MatrixSymmetric<Treal, Tmatrix>&
1202     MatrixSymmetric<Treal, Tmatrix>::operator=
1203     (const XmY<MatrixSymmetric<Treal, Tmatrix>,
1204      MatrixSymmetric<Treal, Tmatrix> >& mmm) {
1205     assert(this != &mmm.B);
1206     (*this) = mmm.A;
1207     Tmatrix::add(-1.0, *mmm.B.matrixPtr, *this->matrixPtr);
1208     return *this;
1209   }
1210   /* B += A */
1211   template<typename Treal, typename Tmatrix>
1212     inline MatrixSymmetric<Treal, Tmatrix>&
1213     MatrixSymmetric<Treal, Tmatrix>::operator+=
1214     (MatrixSymmetric<Treal, Tmatrix> const & A) {
1215     Tmatrix::add(1.0, *A.matrixPtr, *this->matrixPtr);
1216     return *this;
1217   }
1218   /* B -= A */
1219   template<typename Treal, typename Tmatrix>
1220     inline MatrixSymmetric<Treal, Tmatrix>&
1221     MatrixSymmetric<Treal, Tmatrix>::operator-=
1222     (MatrixSymmetric<Treal, Tmatrix> const & A) {
1223     Tmatrix::add(-1.0, *A.matrixPtr, *this->matrixPtr);
1224     return *this;
1225   }
1226   /* B += alpha * A */
1227   template<typename Treal, typename Tmatrix>
1228     inline MatrixSymmetric<Treal, Tmatrix>&
1229     MatrixSymmetric<Treal, Tmatrix>::operator+=
1230     (XY<Treal, MatrixSymmetric<Treal, Tmatrix> > const & sm) {
1231     assert(!sm.tB);
1232     Tmatrix::add(sm.A, *sm.B.matrixPtr, *this->matrixPtr);
1233     return *this;
1234   }
1235 
1236   /* B -= alpha * A */
1237   template<typename Treal, typename Tmatrix>
1238     inline MatrixSymmetric<Treal, Tmatrix>&
1239     MatrixSymmetric<Treal, Tmatrix>::operator-=
1240     (XY<Treal, MatrixSymmetric<Treal, Tmatrix> > const & sm) {
1241     assert(!sm.tB);
1242     Tmatrix::add(-sm.A, *sm.B.matrixPtr, *this->matrixPtr);
1243     return *this;
1244   }
1245 
1246   /** Performs operation specified in 'op' on all nonzero matrix elements
1247    *  and sums up the result and returns it.
1248    *
1249    */
1250   template<typename Treal, typename Tmatrix, typename Top>
accumulate(MatrixSymmetric<Treal,Tmatrix> & A,Top & op)1251     Treal accumulate(MatrixSymmetric<Treal, Tmatrix> & A,
1252 		     Top & op) {
1253     return A.accumulateWith(op);
1254   }
1255 
1256 
1257 
1258 } /* end namespace mat */
1259 #endif
1260 
1261