root/trunk/LinAlg/linalg/MatrixT.d

Revision 152, 33.2 kB (checked in by baxissimo, 4 years ago)

Added the 1-norm and inf-norm.
Improved and documented the polar decomposition routine.

Line 
1 /*==========================================================================
2  * MatrixT.d
3  *    Written in the D Programming Language (http://www.digitalmars.com/d)
4  */
5 /***************************************************************************
6  * Fixed size matrix class (value type)
7  *
8  * The number of rows and columns is fixed at compile time.
9  * Storage is column major like FORTRAN or OpenGL.
10  *
11  * Authors:  William V. Baxter III (OLM Digital, Inc.)
12  * Date: 23 Nov 2007
13  * Copyright: (C) 2007-2008  William Baxter, OLM Digital, Inc.
14  * License: ZLIB/LIBPNG
15  */
16 //===========================================================================
17
18 module linalg.MatrixT;
19 version(Tango) import std.compat;
20
21 import linalg.VectorT;
22
23 import std.math;
24 static import std.string;
25
26 /// Swap two values
27 void swap(T)(inout T a, inout T b) { T t = a; a = b; b = t; }
28
29 /**
30  *  MatrixT is a generic M x N matrix type.
31  *  Dimensions (M rows, N columns) are fixed at compile time.
32  *
33  *  Matrix elements are stored in column-major order
34  *  like FORTRAN or OpenGL.
35  *
36  *  Compatible with linalg.VectorT.
37  */
38 struct MatrixT(T, int M, int N)
39 {
40     alias T Scalar;
41
42     union {
43         Scalar[M*N] values_ = void;
44         static if(M<=10 && N<=10) {
45             mixin(_gen_elements!(M,N)("Scalar"));
46         }
47     }
48
49     /** This contstructor takes M*N elements listed in row-major order.
50      *  Example:
51      *      auto M = MatrixT!(float,2,3)( a00, a01, a02
52      *                                    a10, a11, a12 );
53      */
54     mixin(_gen_constructor!(M,N)("T"));
55
56
57     /** This constructor takes an array of M*N elements listed in row-major order:
58      *  Example:
59      *      auto M = MatrixT!(float,2,3)([ a00, a01, a02
60      *                                     a10, a11, a12 ]);
61      */
62     static MatrixT opCall(T[] v) {
63         assert(v.length == M*N, "Input vector size must be "~ctfe_itoa(M*N));
64         MatrixT Mat;
65         with(Mat) {
66             mixin(_gen_row_major_body!(M,N)("v"));
67         }
68         return Mat;
69     }
70
71     /// Compile-time const identity matrix.
72     mixin(_gen_identity_matrix!(M,N)("static", "identity")) ;
73     mixin(_gen_identity_matrix!(M,N)("static const", "cidentity")) ;
74
75     /// Compile-time const matrix with all elements set to 0.
76     mixin(_gen_zero_matrix!(M,N)("static", "zero")) ;
77     mixin(_gen_zero_matrix!(M,N)("static const", "czero")) ;
78
79
80     /** This constructor takes an array of M*N elements listed in row-major order:
81      *  Example:
82      *      auto M = MatrixT!(float,2,3)([ a00, a01, a02
83      *                                     a10, a11, a12 ]);
84      */
85     static MatrixT row_major(T[] v) {
86         return MatrixT(v);
87     }
88
89     /** This constructor takes an array of M*N elements listed in column-major order:
90      *  Example:
91      *      auto M = MatrixT!(float,2,3)([ a00, a10,
92      *                                     a01, a11,
93      *                                     a02, a12 ]);
94      */
95     static MatrixT col_major(T[] v) {
96         assert(v.length == M*N, "Input vector size must be "~ctfe_itoa(M*N));
97         MatrixT M;
98         M.values_[] = v;
99         return M;
100     }
101
102     /// returns total dimension of the matrix
103     static size_t size() { return M*N; }
104
105     /// Number of rows in the matrix
106     static const int nrows = M;
107
108     /// Number of columns in the matrix
109     static const int ncols = N;
110    
111     /// The shape (rows and columns) of the matrix
112     static uint[2] shape = [M,N];
113
114     // Return a pointer to the elements
115     Scalar* ptr() { return values_.ptr; }
116
117     // Return a pointer to the i,j-th element
118     Scalar* ptr(int i, int j) { return values_.ptr + j*nrows+i; }
119
120     Scalar opIndex(uint row, uint col) {
121         return values_[nrows*col + row];
122     }
123
124     Scalar opIndexAssign(Scalar v, uint row, uint col) {
125         return values_[nrows*col + row] = v;
126     }
127
128     /// Add increment incr to the element [row,col]
129     void add_elem(uint row, uint col, Scalar incr) {
130         return values_[nrows*col + row] += incr;
131     }
132
133     /// Multiply element [row,col] by the given factor
134     void mult_elem(uint row, uint col, Scalar factor) {
135         return values_[nrows*col + row] *= factor;
136     }
137
138     /// -A Unary negation of matrix
139     MatrixT opNeg() {
140         MatrixT ret;
141         for(int i=0; i<size; i++) {
142             ret.values_[i] = -values_[i];
143         }
144         return ret;
145     }
146
147     /// A+B Add two matrices of the same shape
148     MatrixT opAdd(ref MatrixT other) {
149         MatrixT ret; ret = *this; ret += other; return ret;
150     }
151     /// A-B Subtract two matrices of the same shape
152     MatrixT opSub(ref MatrixT other) {
153         MatrixT ret; ret = *this; ret -= other; return ret;
154     }
155
156     /// A+=B
157     void opAddAssign(ref MatrixT other) {
158         for(int i=0; i<size; i++) {
159             values_[i] += other.values_[i];
160         }
161     }
162     /// A-=B
163     void opSubAssign(ref MatrixT other) {
164         for(int i=0; i<size; i++) {
165             values_[i] -= other.values_[i];
166         }
167     }
168
169     /// Or A/s multiplication by a scalar
170     MatrixT opDiv(Scalar v) { return scalar_mult(1/v); }
171
172     /// Or A/=s multiplication by a scalar
173     void opDivAssign(Scalar v) { scalar_mult_assign(1/v); }
174
175
176     /// Return squared Frobinius norm of the matrix
177     Scalar sqrnorm() {
178         Scalar ret=0;
179         foreach(v; values_) ret += v*v;
180         return ret;
181     }
182
183     /// Return Frobinius norm of the matrix
184     Scalar norm() {
185         return sqrt(sqrnorm());
186     }
187
188     /// Return the one-norm of the matrix (sum of elements' absolute values)
189     Scalar onenorm() {
190         Scalar ret=0;
191         foreach(v; values_) ret += abs(v);
192         return ret;
193     }
194
195     /// Return the infinity-norm of the matrix (max absolute value element)
196     Scalar infnorm() {
197         Scalar ret= -Scalar.max;
198         foreach(v; values_) {
199             v = abs(v);
200             if (v>ret) ret = v;
201         }
202         return ret;
203     }
204
205     template IsMatrixT(ArgT) {
206         // Check if it implements a suitable concept of
207         // .ncols, .nrows and indexing with two values
208         static if (is(typeof(ArgT.nrows)) && is(typeof(ArgT.ncols))
209                    && is(typeof(ArgT.init[0,0])))
210         {
211             const bool IsMatrixT = true;
212         }       
213         else {
214             const bool IsMatrixT = false;
215         }
216     }
217     template IsVectorT(ArgT) {
218         // Check if it implements a suitable concept of
219         // .length and indexing with one value
220         static if (is(typeof(ArgT.init[0])) && is(typeof(ArgT.init.length))) {
221             const bool IsVectorT = true;
222         } else {
223             const bool IsVectorT = false;
224         }
225     }
226
227     template MultReturnType(ArgT) {
228         static if (IsMatrixT!(ArgT)) {
229             static if (ncols == ArgT.nrows) {
230                 alias MatrixT!(T,M,ArgT.ncols) MultReturnType;
231             }
232             else {
233                 pragma(msg,"A * B: Shape "~ctfe_itoa(M)~" x "~ctfe_itoa(N)~
234                        " not compatible with "~
235                        ctfe_itoa(ArgT.nrows)~" x "~ctfe_itoa(ArgT.ncols));
236             }
237         }
238         else static if (IsVectorT!(ArgT)) {
239             static if (ncols == ArgT.init.length) {
240                 alias VectorT!(T,M) MultReturnType;
241             }
242             else {
243                 pragma(msg,"A * B: Shape "~ctfe_itoa(M)~" x "~ctfe_itoa(N)~
244                        " not compatible with Vector "
245                        ~ctfe_itoa(ArgT.length));
246             }
247         }
248         else static if (is(ArgT:T)) {
249             alias MatrixT MultReturnType ;
250             }
251         else {
252             pragma(msg, "Unknown type '"~ArgT.stringof~"'for multiplication by MatrixT");
253             alias void MultReturnType ;
254         }
255     }
256
257     /// A*B Linear algebraic multiplication of A * B
258     ///   (Only valid if A.ncols == B.nrows)
259     /// Or A*s multiplication by a scalar
260     MultReturnType!(ArgT) opMul(ArgT)(ArgT other)
261     {
262         MultReturnType!(ArgT) ret;
263         static if (IsMatrixT!(ArgT) && N == ArgT.nrows) {
264             mat_mult_ret!(T,M,N,ArgT.ncols)(*this,other,ret);
265         }
266         else static if (IsVectorT!(ArgT) && N == ArgT.length) {
267             mat_vec_mult_ret!(T,M,N)(*this,other,ret);
268         }
269         else static if (is(ArgT:T)) {
270             ret = scalar_mult(other);
271         }
272         return ret;
273     }
274     /+
275     /// BUG: deduction does not work with DMD 1.023
276     MatrixT!(T,M,P) opMul(int P)(ref MatrixT!(T, N,P) other) {
277         MatrixT!(T,M,P) ret;
278         mat_mult_ret!(T,M,N,P)(*this,other,ret);
279         return ret;
280     }
281     +/
282
283     /// A*=B Linear algebraic multiplication of A * B
284     ///   (Only valid if A.ncols == B.nrows == B.ncols)
285     /// Or A=*s multiplication by a scalar
286     void opMulAssign(ArgT)(ArgT other)
287     {
288         static if (IsMatrixT!(ArgT)) {
289             static if(ncols==ArgT.nrows && ncols==ArgT.ncols) {
290                 MultReturnType!(ArgT) ret;
291                 mat_mult_ret!(T,M,N,ArgT.ncols)(*this,other,ret);
292                 *this = ret;
293             }
294             else {
295                 static assert(false,"A*=B: Shape "~ctfe_itoa(M)~" x "~ctfe_itoa(N)~
296                               " not accumulate compatible with "~
297                               ctfe_itoa(ArgT.nrows)~" x "~ctfe_itoa(ArgT.ncols));
298             }
299         }
300         else static if (is(ArgT:T)) {
301             scalar_mult_assign(other);
302         }
303     }
304
305     MatrixT scalar_mult(Scalar f) {
306         MatrixT ret=void;
307         for(int i=0; i<size; i++) { ret.values_[i] = values_[i]*f; }
308         return ret;
309     }
310
311     void scalar_mult_assign(Scalar f) {
312         for(int i=0; i<size; i++) { values_[i] *= f; }
313     }
314
315     /// Transpose the matrix in place.  Only available for square matrices (M==N).
316     /// For non-sqyare matrices use 'transposed'.
317     static if(M==N) {
318         void transpose() {
319             for (int i=0; i<nrows; i++) {
320                 for (int j=i+1; j<ncols; j++) {
321                     swap(values_[nrows*i + j], values_[nrows*j + i]);
322                 }
323             }
324         }
325     } else {
326         // Make a template so it only asserts if an attempt is made to instantiate
327         void transpose(Dummy=void)() {
328             static assert(false, "transpose(): Only square matrices can be transposed in place");
329         }
330     }
331    
332     /// Return a transposed copy of the matrix.
333     /// For non-sqyare matrices use 'transposed'.
334     MatrixT!(T, N,M) transposed() {
335         MatrixT!(T, N,M) ret = void;
336         for (int i=0; i<nrows; i++) {
337             for (int j=0; j<ncols; j++) {
338                 ret.values_[ncols*i+j] = values_[nrows*j + i];
339             }
340         }
341         return ret;
342     }
343
344
345     static if(M==N) {
346         static if(M==2) {
347             /** Returns the determinant of the matrix */
348             real determinant() {
349                 return m00 * m11 - m10 * m01;
350             }
351             /** Returns the inverse of the matrix */
352             MatrixT inverse() {
353                 MatrixT ret;
354                 ret.m00 =  m11;
355                 ret.m01 = -m01;
356                 ret.m10 = -m10;
357                 ret.m11 =  m00;
358                 real det = (m00 * m11 - m01 * m10);
359                 ret *= 1.0/det;
360                 return ret;
361             }
362             /** Inverts the matrix in place */
363             void invert()
364             {
365                 real idet = 1.0/(m00 * m11 - m01 * m10);
366                 swap(m00,m11);
367                 m10 = -m10;
368                 m01 = -m01;
369                 (*this) *= idet;
370             }
371         }
372         else static if(M==3) {
373             /** Returns the determinant of the matrix */
374             real determinant()
375             {
376                 real cofactor00 = m11 * m22 - m12 * m21;
377                 real cofactor10 = m12 * m20 - m10 * m22;
378                 real cofactor20 = m10 * m21 - m11 * m20;
379            
380                 return m00 * cofactor00 + m01 * cofactor10 + m02 * cofactor20;;
381             }
382             /** Returns the inverse of the matrix */
383             MatrixT inverse()
384             {
385                 MatrixT ret;
386                 ret.m00 = m11 * m22 - m12 * m21;
387                 ret.m01 = m02 * m21 - m01 * m22;
388                 ret.m02 = m01 * m12 - m02 * m11;
389                 ret.m10 = m12 * m20 - m10 * m22;
390                 ret.m11 = m00 * m22 - m02 * m20;
391                 ret.m12 = m02 * m10 - m00 * m12;
392                 ret.m20 = m10 * m21 - m11 * m20;
393                 ret.m21 = m01 * m20 - m00 * m21;
394                 ret.m22 = m00 * m11 - m01 * m10;
395                 real det = m00 * ret.m00 + m01 * ret.m10 + m02 * ret.m20;
396                 ret *= 1.0/det;
397                 return ret;
398             }
399             /** Inverts the matrix "in place" */
400             void invert() { *this = inverse(); }
401         }           
402         else static if (M==4)
403         {
404             /* Returns the determinant of the matrix */
405             real determinant()
406             {
407                 return
408                     + (m00 * m11 - m01 * m10) * (m22 * m33 - m23 * m32)
409                     - (m00 * m12 - m02 * m10) * (m21 * m33 - m23 * m31)
410                     + (m00 * m13 - m03 * m10) * (m21 * m32 - m22 * m31)
411                     + (m01 * m12 - m02 * m11) * (m20 * m33 - m23 * m30)
412                     - (m01 * m13 - m03 * m11) * (m20 * m32 - m22 * m30)
413                     + (m02 * m13 - m03 * m12) * (m20 * m31 - m21 * m30);
414             }
415             /** Returns the inverse of the matrix */
416             MatrixT inverse() {
417                 MatrixT R=MatrixT(
418                     m11*(m22*m33 - m23*m32) + m12*(m23*m31 - m21*m33) + m13*(m21*m32 - m22*m31),
419                     m21*(m02*m33 - m03*m32) + m22*(m03*m31 - m01*m33) + m23*(m01*m32 - m02*m31),
420                     m31*(m02*m13 - m03*m12) + m32*(m03*m11 - m01*m13) + m33*(m01*m12 - m02*m11),
421                     m01*(m13*m22 - m12*m23) + m02*(m11*m23 - m13*m21) + m03*(m12*m21 - m11*m22),
422                     m12*(m20*m33 - m23*m30) + m13*(m22*m30 - m20*m32) + m10*(m23*m32 - m22*m33),
423                     m22*(m00*m33 - m03*m30) + m23*(m02*m30 - m00*m32) + m20*(m03*m32 - m02*m33),
424                     m32*(m00*m13 - m03*m10) + m33*(m02*m10 - m00*m12) + m30*(m03*m12 - m02*m13),
425                     m02*(m13*m20 - m10*m23) + m03*(m10*m22 - m12*m20) + m00*(m12*m23 - m13*m22),
426                     m13*(m20*m31 - m21*m30) + m10*(m21*m33 - m23*m31) + m11*(m23*m30 - m20*m33),
427                     m23*(m00*m31 - m01*m30) + m20*(m01*m33 - m03*m31) + m21*(m03*m30 - m00*m33),
428                     m33*(m00*m11 - m01*m10) + m30*(m01*m13 - m03*m11) + m31*(m03*m10 - m00*m13),
429                     m03*(m11*m20 - m10*m21) + m00*(m13*m21 - m11*m23) + m01*(m10*m23 - m13*m20),
430                     m10*(m22*m31 - m21*m32) + m11*(m20*m32 - m22*m30) + m12*(m21*m30 - m20*m31),
431                     m20*(m02*m31 - m01*m32) + m21*(m00*m32 - m02*m30) + m22*(m01*m30 - m00*m31),
432                     m30*(m02*m11 - m01*m12) + m31*(m00*m12 - m02*m10) + m32*(m01*m10 - m00*m11),
433                     m00*(m11*m22 - m12*m21) + m01*(m12*m20 - m10*m22) + m02*(m10*m21 - m11*m20));
434                 real det = determinant();
435                 R *= 1/det;
436                 return R;
437             }
438             /** Inverts the matrix "in place" */
439             void invert() {  *this = inverse(); }
440         }
441         else {
442             // TODO: implement general NxN case
443             real determinant(Dummy=void)() {
444                 static assert(false, "determinant() not implemented for "~ MatrixT.stringof);
445             }
446             MatrixT inverse(Dummy=void)() {
447                 static assert(false, "inverse() not implemented for "~ MatrixT.stringof);
448             }
449             void invert(Dummy=void)() {
450                 static assert(false, "invert() not implemented for "~ MatrixT.stringof);
451             }
452         }
453     } else {
454         // Non-square matrix
455         real determinant(Dummy=void)() {
456             static assert(false, "determinant() not defined for non-square "~ MatrixT.stringof);
457         }
458         MatrixT inverse(Dummy=void)() {
459             static assert(false, "inverse() not defined for non-square "~ MatrixT.stringof);
460         }
461         void invert(Dummy=void)() {
462             static assert(false, "invert() not defined for non-square "~ MatrixT.stringof);
463         }
464     }           
465
466     string toString() {
467         char[] ret;
468         ret ~= "[";
469         for (int j = 0; j < nrows; j++) {
470             if (j != 0)
471                 ret ~= " [ ";
472             else
473                 ret ~= "[ ";
474             for (int i = 0; i < ncols; i++) {
475                 static if (is(T == struct))
476                     ret ~= std.string.format("%15s",(*this)[j,i].toString);
477                 else if (is(T : int))
478                     ret ~= std.string.format("%7s",(*this)[j,i]);
479                 else
480                     ret ~= std.string.format("%7.4f",(*this)[j,i]);
481                 if (i != ncols -1)
482                     ret ~= ",  ";
483                 else
484                     ret ~= "  ]";
485             }
486             if (j == nrows - 1)
487                 ret ~= "]";
488             else
489                 ret ~= ",";
490             if (j!=nrows-1) ret ~= "\n";
491         }
492         return ret;
493     }
494 }
495
496
497 /// Multiply MxN matrix time NxP matrix
498 void mat_mult_ret(T,int M,int N,int P)(
499     /*const*/ref MatrixT!(T,M,N) A,
500     /*const*/ref MatrixT!(T,N,P) B,
501     inout MatrixT!(T,M,P) ret)
502 {
503     with(ret) {
504         mixin(_gen_mat_mult_body!(M,N,P)("A","B"));
505     }
506 }
507
508 /// Multiply MxN matrix time NxP matrix
509 void mat_mult(T,int M,int N,int P, dum=void )(
510     /*const*/ref MatrixT!(T,M,N) A,
511     /*const*/ref MatrixT!(T,N,P) B,
512     inout MatrixT!(T,M,P) ret)
513 {
514     mat_mult_ret!(T,M,N,P)(A,B,ret);
515 }
516
517 /// Multiply MxN matrix time NxP matrix
518 MatrixT!(T,M,P) mat_mult(T,int M,int N,int P)(
519     /*const*/ref MatrixT!(T,M,N) A,
520     /*const*/ref MatrixT!(T,N,P) B)
521 {
522     MatrixT!(T,M,P) ret;
523     mat_mult_ret!(T,M,N,P)(A,B, ret);
524     return ret;
525 }
526
527 /// Multiply MxN matrix times N-vector (as column) ret = A * x
528 void mat_vec_mult_ret(T,int M,int N)(
529     /*const*/ref MatrixT!(T,M,N) A,
530     /*const*/ref VectorT!(T,N) x,
531     out VectorT!(T,M) ret)
532 {
533     //pragma(msg, _gen_mat_vec_mult_body!(M,N)("A","x"));
534     mixin(_gen_mat_vec_mult_body!(M,N)("A","x"));
535 }
536
537 /// Multiply MxN matrix times N-vector (as column) ret = A * x
538 VectorT!(T,M) mat_vec_mult(T,int M,int N)(
539     /*const*/ref MatrixT!(T,M,N) A,
540     /*const*/ref VectorT!(T,N) x)
541 {
542     VectorT!(T,M) ret=void;
543     mat_vec_mult_ret!(T,M,N)(A,x,ret);
544     return ret;
545 }
546
547 /// Multiply M-vector times MxN matrix (as row) ret = x' * A
548 void vec_mat_mult_ret(T,int M,int N)(
549     /*const*/ref VectorT!(T,M) x,
550     /*const*/ref MatrixT!(T,M,N) A,
551     out VectorT!(T,N) ret)
552 {
553     //pragma(msg, _gen_vec_mat_mult_body!(M,N)("x", "A"));
554     mixin(_gen_vec_mat_mult_body!(M,N)("x","A"));
555 }
556
557 /// Multiply M-vector times MxN matrix (as row) ret = x' * A
558 VectorT!(T,N) vec_mat_mult_ret(T,int M,int N)(
559     /*const*/ref VectorT!(T,M) x,
560     /*const*/ref MatrixT!(T,M,N) A )
561 {
562     VectorT!(T,N) ret=void;
563     return vec_mat_mult_ret!(T,M,N)(x,A,ret);
564     return ret;
565 }
566
567 /// Compute the outer product matrix, ret = a*b'
568 void outer_product_ret(T,int M, int N)(
569     ref VectorT!(T, M) a,
570     ref VectorT!(T, N) b,
571     out MatrixT!(T,M,N) ret)
572 {
573     //pragma(msg, _gen_outer_product_body!(M,N)("a", "b"));
574     mixin(_gen_outer_product_body!(M,N)("a","b"));
575 }
576
577 /// Compute the outer product matrix, ret = a*b'
578 MatrixT!(T,M,N) outer_product(T,int M, int N)(
579     ref VectorT!(T, M) a,
580     ref VectorT!(T, N) b)
581 {
582     MatrixT!(T,M,N) ret=void;
583     outer_product_ret!(T,M,N)(a,b,ret);
584     return ret;
585 }
586
587
588 /** Set A to be a rotation matrix */
589 void rotation2x2(T)(T rads_angle, out MatrixT!(T,2,2) A)
590 {
591     T c = cos(rads_angle);
592     T s = sin(rads_angle);
593     A.m00 = A.m11 = c;
594     A.m10 = s;
595     A.m01 = -s;
596 }
597
598 /** Decomposes A into R*S where R is orthogonal (pure rotation) and
599     S is symmetric (scaling and shearing).
600
601     For matrices A with positive determinant (no reflections),
602     the rotation R is additionally the closest orthogonal matrix to A
603     in the Frobenius norm.
604
605     See
606     1. Shoemake and Duff "Matrix Animation and Polar Decomposition",
607        Proceedings of Graphics Interface, 1992
608
609     2. Higham, "Computing the Polar Decomposition -- with applications",
610        SIAM J. Sci. Stat. Comput, Vol 7,  pp 1160-1174, 1986.
611
612     3. Higham and Schreiber, "Fast Polar Decomposition of an Arbitrary Matrix",
613        SIAM J. Sci. Stat. Comput.  Vol 11, No. 4, pp 648-655, 1990.
614
615     Note: Reference 3 extends the method to non-square matrices.
616           Implementing that is a TODO.
617 */
618 void polar_decomposition(T, int N)(
619     ref MatrixT!(T, N,N) A,
620     out MatrixT!(T, N,N) R,
621     out MatrixT!(T, N,N) S)
622 {
623     alias MatrixT!(T,N,N) Matrix;
624
625     R = A;
626     // Scaled Newton method to orthogonalize R.
627     // Shoemake & Duff 92 just mentions using R := (R+R^-T)
628     // Higham86, and Higham & Schreiber 90
629     // use the scaled Newton version below
630     // that improves convergence significantly.
631     // (For instance, I've seen it reduce 11 iterations down to 4 in
632     //  cases where A has scaling near zero along one axis.)
633
634     int maxIterations = 100;  // shouldn't ever hit this
635     Matrix Rprev = R;
636     R = 0.5f * (R + R.transposed.inverse);
637     while (!((R - Rprev).sqrnorm < 1000*T.epsilon) && maxIterations--)
638     {
639         Matrix Rinv = R.inverse;
640         // Alternate gamma, may (or may not) be a bit slower. 
641         //real gamma = sqrt( sqrt( Rinv.sqrnorm / R.sqrnorm ) );
642         // TODO: measure whether sqrnorm or one*inf norm versions are slower
643         // TODO: replace repeated sqrt with approximate 4th-root function?
644         real gamma = sqrt( sqrt( (Rinv.onenorm * Rinv.infnorm) / (R.onenorm * R.infnorm) ) );
645         Rprev = R;
646
647         //Calc: R = 0.5f * (gamma * R + (1.0 / gamma) * Rinv.transposed);
648         R *= gamma;
649         Rinv.transpose;
650         Rinv /= gamma;
651         R += Rinv;
652         R *= 0.5;
653     }
654            
655     assert( maxIterations != -1 );
656
657     // Using R.T * A + A.T * R makes sure S is symmetric even in
658     // the case where A has a negative determinant.
659     S  = R.transposed * A;
660     S += A.transposed * R;
661     S *= 0.5;
662 }
663
664
665
666 //----------- Private code gen functions ----------------------------
667
668 // Convert from integral type to string.
669 // (Source: Don Clugston's Blade library)
670 private char [] ctfe_itoa(T)(T x)
671 {
672     char [] s="";
673     static if (is(T==byte)||is(T==short)||is(T==int)||is(T==long)) {
674         if (x<0) {
675             return "-" ~ ctfe_itoa(-x);
676         }
677     }
678     do {
679         s = cast(char)('0' + (x%10)) ~ s;
680         x/=10;
681     } while (x>0);
682     return s;
683 }
684
685
686 // Generate the elements m00,m01,m02... elements
687 // For matrices with dimensions < 10 x 10 only.
688 private string _gen_elements(int M, int N)(string type) {
689     static assert(M<=10);
690     static assert(N<=10);
691     char[] Num = "0123456789";
692     string ret;
693     for(int col=0; col<N; ++col) {
694         ret ~= type ~ " ";
695         for (int row=0; row<M; row++) {
696             ret ~= "m" ~ Num[row] ~ Num[col];
697             if (row!=M-1) ret ~=  ", ";
698         }
699         ret ~= ";\n";
700     }
701     return ret;
702 }
703
704 // Generate the opCall constructor taking a list of M*N values
705 // in row-major order. The constructor flips them propperly into
706 // MatrixT's column-major ordering.
707 private string _gen_constructor(int M, int N)(string type) {
708     string ret = "static MatrixT opCall(";
709
710     for (int row=0; row<M; ++row) {
711         for(int col=0; col<N; ++col) {
712             ret ~= type ~ " _" ~ ctfe_itoa(row) ~ "_" ~ ctfe_itoa(col);
713             if (row*col != (N-1)*(M-1)) ret ~=  ", ";
714         }
715     }
716     ret ~= ")";
717     ret ~= "{
718     MatrixT R; with (R) {
719 ";
720     for (int row=0; row<M; ++row) {
721         ret ~= "        ";
722         for(int col=0; col<N; ++col) {
723             ret ~= "values_["~ctfe_itoa(col*M+row)~"] = _" ~ ctfe_itoa(row) ~ "_" ~ ctfe_itoa(col) ~ ";  ";
724         }
725         ret ~= "\n";
726     }
727     ret ~= "    }\n";
728     ret ~= "    return R;\n}";
729     return ret;
730 }
731
732 private string _gen_row_major_body(int M, int N)(string Vals) {
733     string ret;
734     int v=0;
735     for (int row=0; row<M; ++row) {
736         ret ~= "        ";
737         for(int col=0; col<N; ++col,++v) {
738             ret ~= "values_["~ctfe_itoa(col*M+row)~"] = "~Vals~"[" ~ ctfe_itoa(v) ~ "];  ";
739         }
740         ret ~= "\n";
741     }
742     return ret;
743 }
744
745 private string _gen_mat_mult_body(int M, int N, int P)(string A, string B)
746 {
747     string ret;
748     for (int row=0; row<M; ++row) {
749         for(int col=0; col<P; ++col) {
750             ret ~= "        ";
751             ret ~= "values_["~ctfe_itoa(col*M+row)~"] = ";
752             for(int jj; jj<N; ++jj) {
753                 string JJ = ctfe_itoa(jj);
754                 string iA = ctfe_itoa( row+jj *M );
755                 string iB = ctfe_itoa( jj +col*N );
756                 ret ~= A ~ ".values_["~iA~"]*"~B~".values_["~iB~"]";
757                 if (jj != N-1) { ret ~= " + "; }
758                 else { ret ~= ";\n"; }
759             }
760         }
761         ret ~= "\n";
762     }
763     return ret;
764 }
765
766 private string _gen_mat_vec_mult_body(int M, int N)(string A, string x)
767 {
768     string ret;
769     for (int row=0; row<M; ++row) {
770         ret ~= "        ";
771         ret ~= "ret["~ctfe_itoa(row)~"] = ";
772         for(int col=0; col<N; ++col) {
773             string iA = ctfe_itoa( row + col*M );
774             string ix = ctfe_itoa(col);
775             ret ~= A ~ ".values_["~iA~"]*"~x~"["~ix~"]";
776             if (col != N-1) { ret ~= " + "; }
777         }
778         ret ~= ";\n";
779     }
780     return ret;
781 }
782
783 private string _gen_vec_mat_mult_body(int M, int N)(string x, string A)
784 {
785     string ret;
786     for(int col=0; col<N; ++col) {
787         ret ~= "        ";
788         ret ~= "ret["~ctfe_itoa(col)~"] = ";
789         for (int row=0; row<M; ++row) {
790             string iA = ctfe_itoa( row + col*M );
791             string ix = ctfe_itoa( row );
792             ret ~= x~"["~ix~"]*"~ A ~ ".values_["~iA~"]";
793             if (row != M-1) { ret ~= " + "; }
794         }
795         ret ~= ";\n";
796     }
797     return ret;
798 }
799
800
801 private string _gen_outer_product_body(int M, int N)(string a, string b)
802 {
803     string ret;
804     int v=0;
805     for(int col=0; col<N; ++col) {
806         ret ~= "        ";
807         for (int row=0; row<M; ++row,++v) {
808             ret ~= "ret.values_["~ctfe_itoa(v)~"] = ";
809             string ia = ctfe_itoa( row );
810             string ib = ctfe_itoa( col );
811             ret ~= a ~ "["~ia~"]*"~b~"["~ib~"]; ";
812         }
813         ret ~= "\n";
814     }
815     return ret;
816 }
817
818
819
820 private string _gen_identity_matrix(int M, int N)(string storage_class, string name) {
821     string ret = storage_class ~" MatrixT "~name~" = {[cast(T)";
822     for(int col=0; col<N; ++col) {
823         for (int row=0; row<M; ++row) {
824             ret ~= row==col ? "1," : "0,";
825             if (row==M-1 && col!=N-1) ret ~= "  ";
826         }
827     }
828     return ret[0..$-1] ~ "]};";
829 }
830
831 private string _gen_zero_matrix(int M, int N)(string storage_class, string name) {
832     string ret = storage_class ~" MatrixT "~name~" = {[cast(T)";
833     for(int col=0; col<N; ++col) {
834         for (int row=0; row<M; ++row) {
835             ret ~= "0,";
836             if (row==M-1 && col!=N-1) ret ~= "  ";
837         }
838     }
839     return ret[0..$-1] ~ "]};";
840 }
841
842
843 debug (MatrixTMain) {
844     private{
845     alias double num_t;
846     alias MatrixT!(num_t, 3,2) Mat32;
847     alias MatrixT!(num_t, 2,3) Mat23;
848     alias MatrixT!(num_t, 3,3) Mat33;
849     alias MatrixT!(num_t, 2,2) Mat22;
850     alias VectorT!(num_t,3) Vec3;
851     alias VectorT!(num_t,2) Vec2;
852
853     void check_mat(ref Mat33 A, num_t[9] v) {
854         assert(A.m00 == v[0]);
855         assert(A.m01 == v[1]);
856         assert(A.m02 == v[2]);
857         assert(A.m10 == v[3]);
858         assert(A.m11 == v[4]);
859         assert(A.m12 == v[5]);
860         assert(A.m20 == v[6]);
861         assert(A.m21 == v[7]);
862         assert(A.m22 == v[8]);
863     }
864     void check_matf(ref Mat33 A, num_t[9] v) {
865         assert(abs(A.m00 - v[0]) < 1e-4);
866         assert(abs(A.m01 - v[1]) < 1e-4);
867         assert(abs(A.m02 - v[2]) < 1e-4);
868         assert(abs(A.m10 - v[3]) < 1e-4);
869         assert(abs(A.m11 - v[4]) < 1e-4);
870         assert(abs(A.m12 - v[5]) < 1e-4);
871         assert(abs(A.m20 - v[6]) < 1e-4);
872         assert(abs(A.m21 - v[7]) < 1e-4);
873         assert(abs(A.m22 - v[8]) < 1e-4);
874     }
875     void check_mat(ref Mat22 A, num_t[4] v) {
876         assert(A.m00 == v[0]);
877         assert(A.m01 == v[1]);
878         assert(A.m10 == v[2]);
879         assert(A.m11 == v[3]);
880     }
881     void check_mat(ref Mat23 A, num_t[6] v) {
882         assert(A.m00 == v[0]);
883         assert(A.m01 == v[1]);
884         assert(A.m02 == v[2]);
885         assert(A.m10 == v[3]);
886         assert(A.m11 == v[4]);
887         assert(A.m12 == v[5]);
888     }
889     void check_mat(ref Mat32 A, num_t[6] v) {
890         assert(A.m00 == v[0]);
891         assert(A.m01 == v[1]);
892         assert(A.m10 == v[2]);
893         assert(A.m11 == v[3]);
894         assert(A.m20 == v[4]);
895         assert(A.m21 == v[5]);
896     }
897     void check_vec(ref Vec2 x, num_t[2] v) {
898         assert(x[0] == v[0]);
899         assert(x[1] == v[1]);
900     }
901     void check_vec(ref Vec3 x, num_t[3] v) {
902         assert(x[0] == v[0]);
903         assert(x[1] == v[1]);
904         assert(x[2] == v[2]);
905     }
906
907
908 import std.stdio;
909 void main() {
910
911     Mat32 mat32 = [9,8,7,6,5,4];
912     check_mat(mat32, [9,8,
913                       7,6,
914                       5,4]);
915     Mat23 mat23 = {[9,8, 7,6, 5,4]};//col major
916     check_mat(mat23, [9,7,5,
917                       8,6,4]);
918     mat32.m10 = 3;
919     check_mat(mat32, [9,8,
920                       3,6,
921                       5,4]);
922     mat23.m10 = 1;
923     check_mat(mat23, [9,7,5,
924                       1,6,4]);
925
926     //writefln("mat23 =\n%s", mat23);
927
928     MatrixT!(double, 9,9) mat99;
929     MatrixT!(double, 10,10) matxx;
930
931     mat32 = Mat32(1,2,
932                   3,4,
933                   5,6);
934     check_mat(mat32, [1,2,
935                       3,4,
936                       5,6]);
937
938     mat23 = Mat23([1,2,3,
939                    4,5,6]);
940     check_mat(mat23, [1,2,3,
941                       4,5,6]);
942
943     mat32 = Mat32.col_major([1,2,3,4,5,6]);
944     check_mat(mat32, [1,4, 2,5, 3,6]);
945
946     mat23 = Mat23.col_major([1,2, 3,4, 5,6]);
947     check_mat(mat23, [1,3,5,
948                       2,4,6]);
949
950     auto mat33 = mat_mult(mat32,mat23);
951     check_mat(mat33, [9,19,29,
952                       12,26,40,
953                       15,33,51]);
954
955     // opMul
956     auto mat22 = mat23 * mat32;
957     assert(is(typeof(mat22)==Mat22));
958     check_mat(mat22, [22,49, 28,64]);
959    
960     // opMulAssign
961     mat22 = Mat22([1.,2,
962                    5, 3]);
963     mat32 *= mat22;
964     check_mat(mat22, [1,2, 5,3]);
965     check_mat(mat32, [21,14, 27,19, 33,24]);
966
967     // opMul (scalar)
968     mat22 = 2 * mat22;
969     check_mat(mat22, [2,4, 10,6]);
970
971     // opDiv (scalar)
972     check_mat(mat22/2, [1,2, 5,3]);
973
974     // opMulAssign (scalar)
975     mat22 *= 0.5;
976     check_mat(mat22, [1,2, 5,3]);
977
978     // opDivAssign (scalar)
979     mat22 /= 0.5;
980     check_mat(mat22, [2,4, 10,6]);
981
982     mat22 = Mat22([1,2, 5,3]);
983     // opAdd
984     check_mat(mat22+mat22, [2,4, 10,6]);
985     check_mat(mat22, [1,2, 5,3]);
986     check_mat(mat23 + mat23, [2,6,10,  4,8,12]);
987     check_mat(mat23, [1,3,5,  2,4,6]);
988
989     // opSub
990     check_mat(mat22-Mat22(2,4,3,9), [-1,-2, 2,-6]);
991
992     // Transpose / transposed
993     mat22 = Mat22([1,2, 5,3]);
994     check_mat(mat22.transposed, [1,5, 2,3]);
995     check_mat(mat22,            [1,2, 5,3]);
996     mat22.transpose();
997     check_mat(mat22,            [1,5, 2,3]);
998              
999     mat23 = Mat23(1,2,3, 4,5,6);
1000     check_mat(mat23.transposed, [1,4, 2,5, 3,6]);
1001     check_mat(mat23,            [1,2,3, 4,5,6]);
1002     //mat23.transpose();  // compile time error
1003    
1004     mat32 = Mat32(1,2, 3,4, 5,6);
1005     check_mat(mat32.transposed, [1,3,5, 2,4,6]);
1006     check_mat(mat32,            [1,2, 3,4, 5,6]);
1007     //mat32.transpose(); // compile-time error
1008
1009     mat33 = Mat33(1,2,3, 4,5,6, 7,8,9);
1010     check_mat(mat33.transposed, [1,4,7, 2,5,8, 3,6,9]);
1011     check_mat(mat33,            [1,2,3, 4,5,6, 7,8,9]);
1012     mat33.transpose();
1013     check_mat(mat33,            [1,4,7, 2,5,8, 3,6,9]);
1014
1015     mat33.add_elem(2,1, 5.0);
1016     check_mat(mat33,            [1,4,7, 2,5,8, 3,11,9]);
1017
1018     Mat33 mat33inv = mat33.inverse();
1019     check_matf(mat33inv*3, [-4.3, 4.1,-0.3,
1020                             0.6, -1.2, 0.6,
1021                             0.7,  0.1,-0.3]);
1022     check_matf(mat33*mat33inv, [1.,0,0,  0,1,0, 0,0,1]);
1023     check_matf(mat33inv*mat33, [1.,0,0,  0,1,0, 0,0,1]);
1024
1025     check_mat(Mat33.identity, [1.,0,0,  0,1,0, 0,0,1]);
1026     check_mat(Mat33.zero, [0,0,0,  0,0,0, 0,0,0]);
1027    
1028     Vec3 x = {[1,-3,7]};
1029     Vec2 Ax;
1030     mat23 = Mat23(1,2,3, 4,5,6);
1031     mat_vec_mult_ret(mat23, x, Ax);
1032     check_vec(Ax, [16,31]);
1033
1034     vec_mat_mult_ret(Ax, mat23, x);
1035     check_vec(x, [140,187,234]);
1036
1037     x = Vec3d([-1, 2, -3]);
1038     Vec3d y = {[3,2,1]};
1039     outer_product_ret(x, y, mat33);
1040     check_mat(mat33, [-3, -2, -1,       
1041                       +6,  4,  2,
1042                       -9, -6, -3]);
1043    
1044 }}}
1045
1046 //--- Emacs setup ---
1047 // Local Variables:
1048 // c-basic-offset: 4
1049 // indent-tabs-mode: nil
1050 // End:
Note: See TracBrowser for help on using the browser.