1 /*$Id: m_matrix.h,v 26.131 2009/11/20 08:22:10 al Exp $ -*- C++ -*-
2 * Copyright (C) 2001 Albert Davis
3 * Author: Albert Davis <aldavis@gnu.org>
5 * This file is part of "Gnucap", the Gnu Circuit Analysis Package
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, or (at your option)
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.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 *------------------------------------------------------------------
22 * Sparse matrix package
23 * Bump and spike - bordered block diagonal pattern
24 * -------------------------------------------------
25 * To use it (simple method) .....
28 * 2. Then tell it what slots to allocate, in a loop ...
29 * for (all pairs i,j that might exist)
31 * 3. Then do the actual allocation ...
33 * 4. If you want, you can add an offset to the diagonal ...
35 * 5. You probably should set a minimum pivot value ...
36 * a.setminpivot(gmin);
37 * 6. Fill in the matrix ...
38 * for (all pairs i,j you want to fill in)
40 * 7. Then do the LU decomposition ...
42 * 8. Then get the solution by applying the right side vector (v)
43 * and doing the fwd and back substitution.
45 * The solution is now in v.
46 * -------------------------------------------------
48 * Get rid of the old data ...
50 * Restore the offset, if you want ...
52 * Then fill and solve as before. (steps 6-8)
53 * -------------------------------------------------
54 * In the above case, LU replaced the matrix and the solution replaced
55 * the right side, losing the data.
56 * To keep the matrix, and keep the right side ...
57 * 1. Declare two matrices ...
59 * BSMATRIX<double> lu;
61 * 2a. Say the lu has the same structure as a.
64 * 6. Fill in "a" only.
65 * 7. Do the LU decomposition, keeping a, with result in lu ...
66 * lu.lu_decomp(a, false);
67 * 8. Do the f&B sub, keeping b (the right side), with result in x ...
69 * -------------------------------------------------
70 * To make a change to the matrix and re-solve ...
71 * Apply the change to a ...
72 * for (all changes you want to make)
73 * a.m(i,j) += delta; a.set_changed(i).set_changed(j)
74 * Then solve again .. step 7 above for a full solution,
75 * or for an update ...
76 * lu.lu_decomp(a, true);
77 * Then do the same step 8 as above.
78 * -------------------------------------------------
79 * some other functions that might be useful ....
80 * reinit(newsize) -- change the size (and lose the contents and structure)
81 * size()const -- return the size (# of rows & cols)
82 * density()const -- return the matrix density, as a number between 0 and 1
83 * sparsity()const -- 1-density
84 * -------------------------------------------------
85 * individual element access ...
86 * 5 access functions are provided.
87 * All return lvalues (references to the actual entry).
88 * All take 2 args, row and column. They differ in speed and generality.
89 * Since they are used in equations, they all have 1 letter names.
90 * s(r,c) -- "safe" -- most general case, with bounds checking
91 * if completely out of bounds, returns trash
92 * if in the matrix but out of band, returns a reference to zero
93 * Changing it will corrupt the matrix.
94 * All others have no bounds checking.
95 * m(r,c) -- "matrix" -- known to be within the allocated band
96 * u(r,c) -- "upper" -- known to be in the upper triangle. (c>=r)
97 * l(r,c) -- "lower" -- known to be in the lower triangle. (r>=c)
98 * d(r,c) -- "diagonal" -- known to be on the diagonal. (r==c)
99 * Using s() will always work, but will be slower than the other methods.
100 * You should use the most restricted function that you know is correct.
101 * -------------------------------------------------
103 * The numbering starts at 1 (Fortran style).
104 * When using "s" access, it is ok to load row and column 0, then ignore it.
105 * This may simplify load functions, at the expense of run speed.
106 * "s" will let you change the value of zero,
107 * but you will find out about it later.
109 //testing=script 2008.09.19
112 /*--------------------------------------------------------------------------*/
113 #include "l_stlextra.h"
114 /*--------------------------------------------------------------------------*/
118 mutable bool* _changed
;// flag: this node changed value
119 int* _lownode
; // lowest node connecting to this one
120 T
* _space
; // ptr to actual memory space used
121 T
** _rowptr
; // ptrs to col 0 of every row
122 T
** _colptr
; // ptrs to row 0 of every col
123 T
** _diaptr
; // ptrs to diagonal
124 int _nzcount
; // count of non-zero elements
125 int _size
; // # of rows and columns
126 T _zero
; // always 0 but not const
127 T _trash
; // depository for row and col 0, write only
128 T _min_pivot
; // minimum pivot value
130 explicit BSMATRIX(const BSMATRIX
<T
>&) {incomplete();unreachable();}
133 T
& subtract_dot_product(int r
, int c
, int d
);
134 T
& subtract_dot_product(int r
, int c
, int d
, const T
& in
);
135 int lownode(int i
)const {return _lownode
[i
];}
136 bool is_changed(int n
)const {return _changed
[n
];}
137 void set_changed(int n
, bool x
= true)const {_changed
[n
] = x
;}
139 explicit BSMATRIX(int ss
=0);
140 ~BSMATRIX() {uninit();}
141 void reinit(int ss
=0) {uninit(); init(ss
);}
142 //void clone(const BSMATRIX<T>&);
143 void iwant(int, int);
146 void reallocate() {unallocate(); allocate();}
147 void set_min_pivot(double x
) {_min_pivot
= x
;}
150 int size()const {return _size
;}
152 T
d(int r
, int )const {return *(_diaptr
[r
]);}
154 T
u(int r
, int c
)const {return _colptr
[c
][r
];}
155 T
l(int r
, int c
)const {return _rowptr
[r
][-c
];}
160 //T& s(int r, int c);
162 void load_diagonal_point(int i
, T value
);
163 void load_point(int i
, int j
, T value
);
164 void load_couple(int i
, int j
, T value
);
165 void load_symmetric(int i
, int j
, T value
);
166 void load_asymmetric(int r1
, int r2
, int c1
, int c2
, T value
);
168 void lu_decomp(const BSMATRIX
<T
>&, bool do_partial
);
170 void fbsub(T
* v
) const;
171 void fbsub(T
* x
, const T
* b
, T
* c
= NULL
) const;
173 /*--------------------------------------------------------------------------*/
174 // private implementations
175 /*--------------------------------------------------------------------------*/
177 void BSMATRIX
<T
>::uninit()
185 /*--------------------------------------------------------------------------*/
187 void BSMATRIX
<T
>::init(int ss
)
196 _min_pivot
= _trash
= 0.;
199 _lownode
= new int[size()+1];
201 for (int ii
= 0; ii
<= size(); ++ii
) {
204 _changed
= new bool[size()+1];
206 for (int ii
= 0; ii
<= size(); ++ii
) {
207 set_changed(ii
, false);
210 /*--------------------------------------------------------------------------*/
212 T
& BSMATRIX
<T
>::subtract_dot_product(int rr
, int cc
, int dd
)
215 int kk
= std::max(_lownode
[rr
], _lownode
[cc
]);
219 T
* row
= &(l(rr
,kk
));
220 T
* col
= &(u(kk
,cc
));
221 /* for (ii = kk; ii < dd; ++ii) */
222 for (int ii
= 0; ii
< len
; ++ii
) {
223 dot
-= row
[-ii
] * col
[ii
];
229 /*--------------------------------------------------------------------------*/
231 T
& BSMATRIX
<T
>::subtract_dot_product(int rr
, int cc
, int dd
, const T
& in
)
234 int kk
= std::max(_lownode
[rr
], _lownode
[cc
]);
239 T
* row
= &(l(rr
,kk
));
240 T
* col
= &(u(kk
,cc
));
241 /* for (ii = kk; ii < dd; ++ii) */
242 for (int ii
= 0; ii
< len
; ++ii
) {
243 dot
-= row
[-ii
] * col
[ii
];
249 /*--------------------------------------------------------------------------*/
250 // public implementations
251 /*--------------------------------------------------------------------------*/
253 BSMATRIX
<T
>::BSMATRIX(int ss
)
268 /*--------------------------------------------------------------------------*/
270 /* clone: copy to self the structure of another BSMATRIX
271 * this does not copy the values stored in the matrix
274 void BSMATRIX
<T
>::clone(const BSMATRIX
<T
> & aa
)
277 for (int ii
= 0; ii
<= size(); ++ii
) {untested();
278 _lownode
[ii
] = aa
.lownode(ii
);
282 /*--------------------------------------------------------------------------*/
283 /* iwant: indicate that "iwant" to allocate this spot in the matrix
286 void BSMATRIX
<T
>::iwant(int node1
, int node2
)
289 assert(node1
<= size());
290 assert(node2
<= size());
292 if (node1
<= 0 || node2
<= 0) {
293 // node 0 is ground, and doesn't count as a connection
294 // negative is invalid, not used but still may be in a node list
295 }else if (node1
< _lownode
[node2
]) {
296 _lownode
[node2
]=node1
;
297 }else if (node2
< _lownode
[node1
]) {
298 _lownode
[node1
]=node2
;
302 /*--------------------------------------------------------------------------*/
304 void BSMATRIX
<T
>::unallocate()
306 assert (_zero
== 0.);
312 _rowptr
= _colptr
= _diaptr
= NULL
;
315 /*--------------------------------------------------------------------------*/
316 /* allocate: really get the space to work
319 void BSMATRIX
<T
>::allocate()
328 for (int ii
= 0; ii
<= size(); ++ii
) {
329 _nzcount
+= 2 * (ii
- _lownode
[ii
]) + 1;
332 _colptr
= new T
*[size()+1];
333 _rowptr
= new T
*[size()+1];
334 _diaptr
= new T
*[size()+1];
335 _space
= new T
[_nzcount
];
345 for (int ii
= 0; ii
<= size(); ++ii
) {
346 _colptr
[ii
] = point
- _lownode
[ii
];
347 _rowptr
[ii
] = _colptr
[ii
] + 2*ii
;
348 _diaptr
[ii
] = _colptr
[ii
] + ii
;
349 point
+= 2 * (ii
- _lownode
[ii
]) + 1;
353 /*--------------------------------------------------------------------------*/
354 /* zero: wipe the whole array
357 void BSMATRIX
<T
>::zero()
362 std::fill_n(_space
, _nzcount
, 0.);
364 /*--------------------------------------------------------------------------*/
365 /* dezero: make sure(?) the diagonal is non-zero
368 void BSMATRIX
<T
>::dezero(T
& offset
)
370 for (int ii
= 1; ii
<= size(); ++ii
) {
374 /*--------------------------------------------------------------------------*/
376 double BSMATRIX
<T
>::density()
381 for (int ii
= 0; ii
<= size(); ++ii
) {
382 _nzcount
+= 2 * (ii
- _lownode
[ii
]) + 1;
384 return static_cast<double>(_nzcount
-1)/(static_cast<double>(size())*size());
389 /*--------------------------------------------------------------------------*/
390 /* d: fast matrix entry access, lvalue
391 * It is known that the entry is valid and on the diagonal
394 T
& BSMATRIX
<T
>::d(int r
, int c
)
401 return *(_diaptr
[r
]);
403 /*--------------------------------------------------------------------------*/
404 /* u: fast matrix entry access
405 * It is known that the entry is valid and in the upper triangle
408 T
& BSMATRIX
<T
>::u(int r
, int c
)
415 assert(1 <= _lownode
[c
]);
416 assert(_lownode
[c
] <= r
);
418 return _colptr
[c
][r
];
420 /*--------------------------------------------------------------------------*/
421 /* l: fast matrix entry access
422 * It is known that the entry is valid and in the lower triangle
425 T
& BSMATRIX
<T
>::l(int r
, int c
)
432 assert(1 <= _lownode
[r
]);
433 assert(_lownode
[r
] <= c
);
435 return _rowptr
[r
][-c
];
437 /*--------------------------------------------------------------------------*/
438 /* m: semi-fast matrix entry access
439 * It is known that the entry refers to a valid location
440 * but it is not known whether lower, upper, or diagonal
443 T
& BSMATRIX
<T
>::m(int r
, int c
)
445 return (c
>=r
) ? u(r
,c
) : l(r
,c
);
447 /*--------------------------------------------------------------------------*/
448 /* s: general matrix entry access.
449 * It is known that the location is strictly in bounds,
450 * but it is not known whether the location actually exists.
451 * If access is attempted to a non-allocated location,
452 * it returns a reference to a shared zero variable.
453 * Writing to this zero is not prohibited,
454 * but will corrupt the matrix in a known and testable way.
455 * If access is attempted to row 0 or column 0,
456 * it returns a reference to a shared trash variable.
457 * Writing to trash is allowed and encouraged,
458 * but reading it gives a number not useful for anything.
462 T
& BSMATRIX
<T
>::s(int row
, int col
)
466 assert(col
<= size());
468 assert(row
<= size());
471 if (col
== row
) {untested();
473 }else if (col
> row
) {untested(); /* above the diagonal */
474 if (row
== 0) {untested();
476 }else if (row
< _lownode
[col
]) {untested();
481 }else{untested(); /* below the diagonal */
483 if (col
== 0) {untested();
485 }else if (col
< _lownode
[row
]) {untested();
494 /*--------------------------------------------------------------------------*/
496 void BSMATRIX
<T
>::load_point(int i
, int j
, T value
)
498 if (i
> 0 && j
> 0) {
505 /*--------------------------------------------------------------------------*/
506 // load_point(i, i, value);
508 void BSMATRIX
<T
>::load_diagonal_point(int i
, T value
)
516 /*--------------------------------------------------------------------------*/
517 // load_point(i, j, -value);
518 // load_point(j, i, -value);
520 void BSMATRIX
<T
>::load_couple(int i
, int j
, T value
)
533 /*--------------------------------------------------------------------------*/
534 // load_point(i, i, value); or load_diagonal_point(i, value);
535 // load_point(j, j, value); or load_diagonal_point(j, value);
536 // load_point(i, j, -value);
537 // load_point(j, i, -value);
539 void BSMATRIX
<T
>::load_symmetric(int i
, int j
, T value
)
557 /*--------------------------------------------------------------------------*/
558 // load_point(r1, c1, value);
559 // load_point(r2, c2, value);
560 // load_point(r1, c2, -value);
561 // load_point(r2, c1, -value);
563 void BSMATRIX
<T
>::load_asymmetric(int r1
,int r2
,int c1
,int c2
,T value
)
592 /*--------------------------------------------------------------------------*/
594 void BSMATRIX
<T
>::lu_decomp(const BSMATRIX
<T
>& aa
, bool do_partial
)
596 int prop
= 0; /* change propagation indicator */
599 assert(aa
.size() == size());
600 for (int mm
= 1; mm
<= size(); ++mm
) {
601 assert(aa
.lownode(mm
) == _lownode
[mm
]);
602 int bn
= _lownode
[mm
];
603 if (!do_partial
|| aa
.is_changed(mm
) || bn
<= prop
) {
604 aa
.set_changed(mm
, false);
608 u(bn
,mm
) = aa
.u(bn
,mm
) / d(bn
,bn
);
609 for (int ii
= bn
+1; ii
<mm
; ii
++) {
610 /* u(ii,mm) = (aa.u(ii,mm) - dot(ii,mm,ii)) / d(ii,ii); */
611 subtract_dot_product(ii
,mm
,ii
,aa
.u(ii
,mm
)) /= d(ii
,ii
);
613 l(mm
,bn
) = aa
.l(mm
,bn
);
614 for (int jj
= bn
+1; jj
<mm
; jj
++) {
615 /* l(mm,jj) = aa.l(mm,jj) - dot(mm,jj,jj); */
616 subtract_dot_product(mm
,jj
,jj
,aa
.l(mm
,jj
));
619 /* d(mm,mm) = aa.d(mm,mm) - dot(mm,mm,mm); then test */
620 if (subtract_dot_product(mm
,mm
,mm
,aa
.d(mm
,mm
)) == 0.) {untested();
621 error(bWARNING
, "open circuit: internal node %u\n", mm
);
622 d(mm
,mm
) = _min_pivot
;
626 }else{ /* bn == mm */
627 d(mm
,mm
) = aa
.d(mm
,mm
);
628 if (d(mm
,mm
)==0.) {untested();
629 d(mm
,mm
) = _min_pivot
;
637 /*--------------------------------------------------------------------------*/
639 void BSMATRIX
<T
>::lu_decomp()
642 for (int mm
= 1; mm
<= size(); ++mm
) {
643 int bn
= _lownode
[mm
];
645 u(bn
,mm
) /= d(bn
,bn
);
646 for (int ii
=bn
+1; ii
<mm
; ii
++) {
647 /* (m(ii,mm) -= dot(ii,mm,ii)) /= d(ii,ii); */
648 subtract_dot_product(ii
,mm
,ii
) /= d(ii
,ii
);
650 for (int jj
= bn
+1; jj
<mm
; jj
++) {
651 /* m(mm,jj) -= dot(mm,jj,jj); */
652 subtract_dot_product(mm
,jj
,jj
);
655 /* m(mm,mm) -= dot(mm,mm,mm); then test */
656 if (subtract_dot_product(mm
,mm
,mm
) == 0.) {untested();
657 error(bWARNING
, "open circuit: internal node %u\n", mm
);
658 d(mm
,mm
) = _min_pivot
;
662 }else{ /* bn == mm */
664 d(mm
,mm
) = _min_pivot
;
670 /*--------------------------------------------------------------------------*/
671 /* fbsub: forward and back sub, shared storage
672 * v = right side vector, changed in place to solution vector
675 void BSMATRIX
<T
>::fbsub(T
* v
) const
680 for (int ii
= 1; ii
<= size(); ++ii
) { /* forward substitution */
681 for (int jj
= _lownode
[ii
]; jj
< ii
; ++jj
) {
682 v
[ii
] -= l(ii
,jj
) * v
[jj
];
687 for (int jj
= size(); jj
> 1; --jj
) { /* back substitution */
688 for (int ii
= _lownode
[jj
]; ii
< jj
; ++ii
) {
689 v
[ii
] -= u(ii
,jj
) * v
[jj
];
693 /*--------------------------------------------------------------------------*/
694 /* fbsub: forward and back sub, separate storage
695 * b = right side vector
696 * c = intermediate vector after fwd sub
697 * x = solution vector
700 void BSMATRIX
<T
>::fbsub(T
* x
, const T
* b
, T
* c
) const
709 for ( ; ii
<= size(); ++ii
) {
718 for ( ; ii
<= size(); ++ii
) { /* forward substitution */
719 int low_node
= std::max(_lownode
[ii
], first_nz
);
721 for (int jj
= low_node
; jj
< ii
; ++jj
) {
722 c
[ii
] -= l(ii
,jj
) * c
[jj
];
728 notstd::copy_n(c
, size()+1, x
);
730 for (int jj
= size(); jj
> 1; --jj
) { /* back substitution */
731 for (int ii
= _lownode
[jj
]; ii
< jj
; ++ii
) {
732 x
[ii
] -= u(ii
,jj
) * x
[jj
];
736 //BUG// some things don't work unless there is a zero here.
738 /*--------------------------------------------------------------------------*/
739 /*--------------------------------------------------------------------------*/
742 // vim:ts=8:sw=2:noet: