CVM Library
Overview
The CVM library is a library for complex numbers, dense vectors and dense matrices written in ANSI C++ 98. So it should be portable to your compiler and platform. It is designed to give an intuitive interface to these objects in much the same semantics as the built-in C++ types. (Yes, the name came from CVMT which is the complex number, vector, matrix and tensor library, but I don’t understand tensors enough to write that code. If someone would like to explain them to me email me at jasonar81@yahoo.com.) I wrote this library because I needed a complex matrix class, but everything I could find on the internet had unwieldy syntax or would not build on my system (or wasn’t free).
The library is designed in such as fashion that you should be able to start right in using these new types exactly as you would expect to. If you however insist on the details:
ANCHOR Installation installation
Installation instructions are in INSTALL. If the ./configure etc... route won’t work for installation, read README. Otherwise, follow the directions in INSTALL. Then put #include <cvm.h> in your program and use -lcvm after the source file name to link with the library if you are using g++ (gcc). This will link your program with the static version of the library. If you wish to link with the shared version of the library you would have to do something like:
libtool g++ -o your_file your_file.cc -lcvm -R /usr/local/lib
If you are using a different compiler, you will have to look at the documentation for the compiler to figure out how to link with the library, or read README for the simplest possible way of making things work.
Classes
The included classes are:
ShortComplex
IntComplex
LongComplex
FloatComplex
DoubleComplex
LongDoubleComplex
ShortVector
ShortComplexVector
IntVector
IntComplexVector
LongVector
LongComplexVector
FloatVector
FloatComplexVector
DoubleVector
DoubleComplexVector
LongDoubleVector
LongDoubleComplexVector
ShortMatrix
ShortComplexMatrix
IntMatrix
IntComplexMatrix
LongMatrix
LongComplexMatrix
FloatMatrix
FloatComplexMatrix
DoubleMatrix
DoubleComplexMatrix
LongDoubleMatrix
LongDoubleComplexMatrix
CVMError
Details
CVMError
CVMError is the type of object that gets thrown when an error is encountered in the vector or matrix routine. The complex number routines do not throw errors.
Complex Floating Point Classes
Constructors
The complex floating point types have default constructors (which initializes them to 0). They also can be created from the built-in types which allows them to work perfectly in expressions that are partly real partly complex. Values will automatically be converted to the proper types. That is any built-in data type will be converted to its corresponding complex type if necessary. Different complex types can also be mixed in expressions. They will be converted as necessary as long as that conversion is upwards: short -> int -> long - > float -> double -> long double. No downward conversion will occur. LongDoubleComplex types will not convert to DoubleComplex for an assignment as the corresponding built-in types will in C++. There is also an explicit constructor that takes (real, imag).
Operators
The operators +=, -=, *=, /=, =, -, +, *, /, ==, !=, <<, >> exist for all of the complex number types and behave exactly as they do for the built-in types.
Math Functions
exp, cos, cosh, sin, sinh, tan, tanh, sqrt, pow, acos, acosh, asin, asinh, atan, atanh, log are all provided.
ANCHOR arg() Arg()
arg()
arg() takes a complex number and returns Arg, the principal value of the angle of the complex number.
ANCHOR Conj() conj()
conj()
conj() takes a complex number and returns its complex conjugate.
ANCHOR Ln() ln()
ln()
ln() provides the natural log for any complex number (log() does the same thing). Furthermore if ln is passed a negative real number, it will handle it and return a FloatComplex.
ANCHOR norm() abs()
abs() and norm() both take a complex number. Abs() returns sqrt(real^2 + imag^2). Norm() returns abs^2.
ANCHOR Polar() polar()
polar()
polar(r, theta) returns the complex number (in rectangular format) associated with the polar format number.
ANCHOR Read() read()
read()
read(fstream, complex number) takes an fstream and a complex number. It reads a complex number into the complex number variable from a binary fstream.
ANCHOR real() imag()
The complex number objects have real() and imag() member functions that return the real and imaginary parts of the numbers respectively. They actually return a reference, so they can be used on the left hand side of an assignment as well.
ANCHOR Write() write()
write()
write(fstream, complex number) takes an fstream and a complex number. It write the complex number to the binary fstream.
Integer Complex Types
The integer complex types behave the same as the floating point complex types, but they are more restricted. Division operations with integer complex types only do integer division. Furthermore, if you want to call one of the functions listed under math functions for an integer complex type, you must first static cast the complex number to a floating point complex number type. This means that the math function will then return a floating point complex type which cannot be assigned back to an integer complex type. The read() and write() functions do exist for the integer complex types.
Floating Point Matrix Types
Constructors
Each matrix type has a default constructor that creates a 1 X 1 matrix. Matrices will automatically resize as necessary upon assignment. There is also an explicit constructor that takes (rows, columns). Just as with the complex numbers, matrix types will automatically be upwardly converted as necessary (see Floating Point Complex Types: Constructors). Furthermore, complex matrix types can be created out of real matrix types as long as the conversion is still upwards.
operator +=
Each matrix type has a += operator. This operator will throw a CVMError if there is a matrix size mismatch.
operator -=
Each matrix type has a -= operator. This operator will throw a CVMError if there is a matrix size mismatch.
operator *=
Each matrix type has a *= operator. The right hand side can be either another matrix (or a vector see below) or a real (or complex) number. This operator will throw a CVMError if there is a matrix size mismatch.
operator /=
Each matrix type has a /= operator. The right hand side of this operator needs to be a real (or complex) number.
A couple other operators
Each matrix type has assignment, unary -, and unary + operators.
Some more operators
The binary operators + and – are overloaded for the matrix types. A CVMError will be thrown if there is a matrix size mismatch. The multiplication operator is also overloaded for matrix-matrix multiplication as well as pre and post multiplication by a real (or complex) number. Obviously the matrix-matrix multiplication can throw a CVMError if there is a size mismatch. Matrices can also be divided by a scalar value. Also present is (scalar value) / (matrix), which performs (scalar value) * (matrix)^-1. Please note that this does call inverse(). The == and != operators have also been overloaded for the matrix types. You can test for equality (or inequality) with unequal size matrices. They will just be considered unequal. The input operator (>>) is also present. The first values entered becomes the number of rows. The second value becomes the number of columns, and then the matrix elements are entered in row major order. Finally, the output operator (<<) is overloaded as well (see set_width() and get_width()). Note that the operators are reserved for real mathematical operations, that is * represents matrix multiplication not element-wise multiplication, and a matrix cannot be divided by another matrix.
operator []
The subscript operator returns a row vector of the matrix. This allows matrix[row][col] to then return a specific element of the matrix. Actually both of these subscript operators return references allowing you to set a particular element of a matrix or to do things like assign a row of a matrix from a vector (vectors are implemented as row vectors see below). This function will throw a CVMError if the index is out of bounds. All indexing is zero based.
abs()
abs() takes a matrix as its argument and returns the matrix where the abs() function has been applied element-wise to abs()’s argument. The argument to abs() does not get modified in any way. Abs() exists for all floating point matrix types. There is no need for any silly prefixing like fabs() or labs() or whatever. Also note that for complex matrices this function returns a real matrix since abs() of complex number is a real number (see abs() in the complex number section above).
ANCHOR addcol() mulcol()
addcol(). mulcol(), and swapcol() work exactly like addrow(), mulrow(), and swaprow() except with columns.
ANCHOR Addrow() addrow()
addrow()
addrow(dest row, coeff, source row) is a member function that does dest row += coeff * source row. This function returns a reference to the calling object. If any indexes are out of bounds, a CVMError is thrown. Note that this functions uses zero based indexes. There is also a non-member version of this function that takes a matrix as its first argument. It returns the resultant matrix, but does not modify its argument.
ANCHOR Augment() augment()
augment()
augment() is a member function that takes another matrix as its argument. augment() actually attaches the argument matrix onto the right hand side of the calling object. These two matrices must have the same number of rows or a CVMError object will be thrown. augment() returns a reference to the calling object.
conj() is a function that takes a complex matrix as an argument and returns the matrix where every element has been converted to its complex conjugate.
ANCHOR Det() det()
det()
det() takes a matrix as its argument. det() returns the determinant of the argument matrix. det() will throw a CVMError if the argument matrix is not square.
ANCHOR Diag() diag()
diag()
Every matrix class includes a static member function diag(values[], size) that returns the size X size diagonal matrix that has values[] as the elements on the main diagonal. matrix[0][0] is set to values[0], matrix[1][1] is set to values[1], and so on.
ANCHOR Divide() divide()
divide()
divide() is a member function with the same semantics as multiply() except that element-wise division occurs.
ANCHOR Eigenvalues() eigenvalues()
eigenvalues()
eigenvalues() takes a matrix as its argument. If the argument matrix is not square, a CVMError is thrown. eigenvalues() returns a n X 1 matrix (a column vector, not to be confused with the included Vector classes) of which each element is an eigenvalue of the argument matrix. Note that this returned matrix is always complex, and that eigenvalues() will find both complex and real eigenvalues. The eigenvalues() algorithm is iterative, so use set_iter() as needed. Also, the algorithm terminates when it creates an upper triangular matrix (that is almost true). So use set_tol() as necessary to change what is considered an upper triangular matrix (when entries should be considered zero). If the alogrithm fails to converge, a CVMError will be thrown. eigenvalues() does check all computed eigenvalues prior to returning and sets any of them to zero if they are considered equal to zero under the current setting of the appropriate tolerance variable (see set_float_tol() and set_double_tol()). This seems to improve accuracy.
ANCHOR Eigenvectors() eigenvectors()
eigenvectors()
eigenvectors(matrix, eigenvalues*) computes the eigenvectors of the argument “matrix”. The second argument, “eigenvalues” is optional. If provided, it must be a pointer to an n X 1 matrix that contains the eigenvalues of argument “matrix”. If you have already computed the eigenvalues you should pass this argument as it will save computation. eigenvectors() returns a complex matrix such that each row is an eigenvector of the argument “matrix”. These eigenvectors are ordered such that the eigenvector in the ith row of the returned matrix corresponds to the eigenvalue in the ith row of the matrix pointed to by the “eigenvalues” parameter or the eigenvalues matrix returned by the eigenvalues() function (if you do not give a second argument). If you pass incorrect eigenvalues to this function, it will still return correct eigenvectors, but there may be duplicated, some may be missing, and the function may not converge as it is iterative. As the function is iterative, it does use the iters parameter (see set_iter()); however, this algorithm usually has no problem converging (if it did not converge it would throw a CVMError). If the argument “matrix” is not square, a CVMError is thrown. This function calls eigenvalues() if you do not specify a second argument, so look at that and set_tol(). Also, if the eigenvalues parameter does not point to a matrix of the appropriate size, a CVMError is thrown.
ANCHOR Eye() eye()
eye()
Every matrix class includes a static member function eye(size) that returns the size X size identity matrix.
ANCHOR Fill() fill()
fill()
fill(value) is a member function that fills the entire matrix with value.
ANCHOR Frobenius() frobenius()
frobenius()
frobenius() is a member function that returns the frobenius norm of the calling matrix.
ANCHOR get_double_tol() set_double_tol()
get_double_tol() set_double_tol()
These functions play the same roles as get_float_tol() and set_float_tol() except they modify the behavior of the double and long double matrix classes.
ANCHOR get_float_tol() set_float_tol()
get_float_tol() set_float_tol()
These functions set or get the tolerance that is associated with the FloatMatrix and FloatComplexMatrix classes. This tolerance is used in the following manner: If two floating point values are tested for equality (usually we are testing == 0) they are considered equal if the smallest relative error possible (if it is true that the two are in fact equal) is less than the tolerance. The exception to this is if the absolute error is less than the relative error, then two numbers are considered equal if the absolute error is less than the tolerance. These functions are not members of any class as the tolerance variable is global to the Float<Complex>Matrix classes. The are several functions which use this tolerance value (mainly eigenvalues()), and it will be stated in the description for those functions.
ANCHOR get_iter() set_iter()
Each matrix type has its own static member variable “iter” which specifies the maximum number of times for the eigenvalues() and eigenvectors() functions to iterate before giving up. get_iter() and set_iter() are accessor and mutator functions respectively for this static member variable. The default value is 100.
ANCHOR Hessenberg() hessenberg()
hessenberg()
hessenberg() takes a matrix as its argument. It returns a matrix which is the argument matrix converted to hessenberg form (all entries are zero below the first subdiagonal). The argument matrix is not modified in any way. This function is mainly used by the eigenvalues() function as a matrix and its hessenberg form have the same eigenvalues. hessenberg() will throw a CVMError if the argument matrix is not square.
ANCHOR Infnorm() infnorm()
infnorm()
infnorm() is a member function that returns the infinite norm of the calling matrix.
ANCHOR Inverse() inverse()
inverse()
inverse() behaves the same as invert() except that it returns the inverse as a new new matrix and does not modify the calling object (and thus does not return a reference).
ANCHOR Invert() invert()
invert()
invert() is a member function that inverts the calling matrix object. Note that this does modify the calling object (and thus returns a reference. If you wish to compute the inverse without modifying the calling object use inverse(). A CVMError will occur if the calling matrix object is not square or if the matrix is singular (in which case solve() will actually end up throwing the error).
ANCHOR Is_symmetric() is_symmetric()
is_symmetric()
is_symmetric() is a member function that return true or false depending on whether the calling object is a symmetric matrix. Note that strict equality is used (in this case) even for floating point numbers.
ANCHOR LU()
LU()
LU() takes a matrix as its argument. It returns the LU decomposition of the argument such that argument = L * U. Note that LU will throw a CVMError if the argument is not a square matrix. A CVMError will also be thrown if the matrix is not LU-factorable, that is the argument matrix will not be permuted to make it LU-factorable (see PLU() below). LU() returns the matrix L | U. From now on the notation L | U will represent the matrix that is L augmented by U (see augment() above).
ANCHOR max_col_index() min_col_index()
max_col_index() and min_col_index()
max_col_index() and min_col_index() behave the same as max_row_index() and min_row_index() except they deal with columns. Note that complex matrix classes do not have these functions.
ANCHOR max_row_index() min_row_index()
max_row_index() and min_row_index()
max_row_index(row) and min_row_index(row) are member functions that return the zero based index of the maximum or minimum value in the desired row of the calling object. If the row requested is out of bounds a CVMError is thrown. Note that complex matrix classes do not have these functions.
ANCHOR min() max()
min() and max() are member functions that return the value of the smallest or largest valued element of the calling matrix respectively. Note that complex matrix classes do not have these functions.
ANCHOR Mulrow() mulrow()
mulrow()
mulrows(row, coeff) is a member function that muliplies row by coeff. This function returns a reference to the calling object. If any indexes are out of bounds, a CVMError is thrown. Note that this function uses zero based indexes. There is also a non-member version of this function that takes a matrix as its first argument. It returns the reultant matrix, but does not modify its argument.
ANCHOR Multiply() multiply()
multiply()
multiply() is a member function that takes another matrix as its argument. multiply() does element-wise multiplication. That is, calling object[row][col] *= argument[row][col]. This function returns a reference to the calling (changed) object. Please note that from now on it will be implicit that any member functions which change the calling object return references to that object. The calling object and the argument matrix need to be the same size or a CVMError is thrown. There is also a non-member version of this function that takes two matrices. The resultant matrix is returned, but neither argument is modified.
ANCHOR Onenorm() onenorm()
onenorm()
onenorm() is a member functions that returns the one-norm of the calling matrix. (two-norms are explained later).
ANCHOR Part() part()
part()
part(starting row, starting col, ending row, ending col) is a member function that returns a matrix which is the rectangular submatrix of the calling object that includes the elements between (starting row, starting col) and (ending row, ending col) inclusive. These parameters are zero based indexes. part() will throw a CVMError if any of the indexes are out of bounds. The calling object is not changed in any way.
ANCHOR PLU()
PLU()
PLU() takes a matrix as its argument. It returns the LU decomposition of the argument such that P * argument = L * U, where P is a permutation matrix. Note that PLU() with throw a CVMError if the argument is not a square matrix. A CVMError is also thrown if the argument matrix is singular. For all nonsingular matrices, PLU() will succeed. PLU() returns the matrix P | L | U.
ANCHOR Pow() pow()
pow()
pow(matrix, power) is a function that returns “matrix” raised to the “power” power. “power” can be any real or complex number as pow always returns a complex matrix anyway. Pow() will throw a CVMError if “matrix” is not a square matrix. Note that pow() calls eigenvalues(), eigenvectors(), and solve().
ANCHOR Power() power()
power()
power() is a member function with the same semantics as multiply() (see above) except that the computation done is calling object[row][col] = pow(calling object[row][col], argument[row][col]).
ANCHOR QR()
QR()
QR() takes a matrix as its argument. QR() returns the QR factorization of the argument matrix, where argument = Q * R. When factoring an m X n matrix, QR() factors the argument into Q which is m X m and R which is m X n. QR() returns Q | R. Before returning QR() looks for elements of the resultant matrix that are close to zero. If the elements are considered zero by the current setting of float_tol or double_tol, they will be set to zero (see set_float_tol() etc.). This is mainly to improve the performance of the eigenvalues() function.
ANCHOR Read() read()
read()
read(fstream, matrix) takes an fstream and a matrix. It reads a matrix into the matrix variable from the binary fstream. The matrix variable will be resized if necessary.
ANCHOR Resize() resize()
resize() is a member function that resize the given matrix to the indicated size. Matrix elements are kept intact when possible. New elements are initialized to zero.
ANCHOR rows() cols()
rows() and cols() are member functions which return the number of rows and the number of columns in the matrix respectively. rows() and cols() return the actual number of rows and columns, not the maximum zero based index.
ANCHOR set_width() get_width()
set_width(new width) and get_width are static member functions. Each matrix type has its own set_width() and get_width. These functions are accessor and mutator functions for “width” which is a static class variable. Basically when a matrix is being output with the output operator, a setw() is inserted in the output stream directly before outputting each matrix element. This width becomes the argument of that setw() function. The default value of “width” is 8.
ANCHOR Solve() solve()
solve()
solve() takes a matrix as its argument. solve() solves the equation A * x = rhs (where x and rhs are n X 1 matrices). The matrix argument is an augmented matrix such that the rightmost column is the right hand side of the system of linear equations (A | rhs). solve() will throw a CVMError if the linear system does not have a unique solution. Solve() returns x as an n X 1 matrix.
ANCHOR Sqrt() sqrt()
sqrt()
sqrt() takes a matrix as its argument and returns the actual square root of the matrix, that is a new matrix such that matrix multiplication of itself by itself would be equal to the argument to the sqrt() function. Note that this function any many to follow call other functions and therefore can throw CVMErrors through the other functions they call. Always check the documentation for the other functions to see what CVMErrors they throw. In this case sqrt() calls pow(). Please note that sqrt() always returns a complex matrix.
ANCHOR Stack() stack()
stack()
stack() is a member function that takes another matrix as its argument. stack() actually stacks the calling object matrix on top of the argument matrix (the matrix that is changed is the calling object). These two matrices must have the same number of columns or a CVMError object will be thrown. stack() returns a reference to the calling object.
ANCHOR sumrow() sumcol()
sumrow(row) and sumcol(col) are member functions that return the sum of all of the elements in the specified row (or column) of the calling matrix. If the row (or column) specified is out of bounds, a CVMError is thrown. There are also non-member versions of these functions that take a matrix as their first arguments.
ANCHOR Swaprow() swaprow()
swaprow()
swaprow(row a, row b) is a member function that swaps rows a and b of the calling object. This function returns a reference to the calling object. If any indexes are out of bounds, a CVMError is thrown. From now on understand that any functions which take row indexes or column indexes use zero based indexes. There is also a non-member version of this function that takes a matrix as its first argument. It returns the resultant matrix, but does not modify its argument.
ANCHOR T() t()
t()
t() is a member function that returns the transpose of the matrix.
ANCHOR Trace() trace()
trace()
trace() is a function that takes a matrix as its argument. It returns the trace (the sum of the elements on the main diagonal) of the matrix. This function will throw a CVMError if the matrix is not square.
ANCHOR Twonorm() twonorm()
twonorm()
twonorm() is a member function that returns the two-norm of the calling matrix object. twonorm() does call eigenvalues().
ANCHOR Write() write()
write()
write(fstream, matrix) takes an fstream and a matrix. It writes the matrix to the binary fstream. Size information is stored with the matrix element data.
Integer Matrix Types
The integer matrix types behave in much the same way as the above mentioned floating point matrix types; however, they are very restricted. The following functions are not available to integer matrix types (they would have to be cast to a floating point matrix type):
any norms
(scalar) / (matrix)
abs()
det()
LU()
PLU()
pow()
QR()
Floating Point Vector Types
The most important thing to understand about vectors is that they are matrices. In fact the vector classes are derived from the matrix classes. This means that vectors will behave as matrices when necessary. Although operations on vectors will return vector types whenever the result is still a vector. There are some functions which have been overloaded to give slightly different operation for vectors. Those will be described below. There are also some functions that cannot be used on vectors:
swapcol()
This points out the second most important thing about the vector classes. Vectors cannot be resized, and will not resize automatically upon assignment as the matrix classes will.
Constructors
The vector classes have a default constructor that creates a 1-vector. This is almost certainly not what you want since vectors cannot be resized. There is also an explicit constructor which takes as an argument the integer size of the vector. Vectors can also be created from other vector types as long as the conversion is upward (see complex numbers: constructors and floating point matrix types: constructors).
Vectors are row vectors. This means that if you are doing math with the assumption that they are column vectors, you will need to use the transpose function as necessary.
operator *
The multiplication operator performs the dot product of two vectors. If they are not of the same size, a CVMError will be thrown. Note that since vectors are matrices, matrix-matrix, vector-matrix, matrix-vector, and vector-vector multiplication all work correctly just by using the multiplication operator as the meaning can be inferred by the compiler.
operator >>
The input operator works slightly differently with vectors. Since vectors cannot be resize, there is no need to enter the vector size. The input operator just accepts as many values as will fit in the vector given its current (and final) size.
operator []
The subscript operator returns a reference to the desired element of the vector. Indexes are zero based.
A CVMError will be thrown if the index is out of bounds.
ANCHOR Angle() angle()
angle()
angle(vector a, vector b) returns the angle (in radians) between the two vectors. If the vectors are not the same size, a CVMError is thrown.
ANCHOR Cross() cross()
cross()
cross(vector a, vector b) returns the cross product a X b. If a and b are not both three-dimensional a CVMError will be thrown.
infnorm() is a member function that returns the infinity norm of the calling vector object.
ANCHOR Length() length()
length()
length() is a member function that returns the 2-norm of the calling vector object.
ANCHOR Outer() outer()
outer()
outer(vector a, vector b) returns the outer product of a and b (it’s a matrix)! If the vectors are not of the same size, a CVMError will be thrown. Note that since vectors are implemented as row vectors this actually computer aT * b.
ANCHOR Pnorm() pnorm()
pnorm()
pnorm(int) is a member function that returns the p-norm of the calling vector object. The integer argument must be positive (it is unsigned as almost all integers are in this entire library). If you specify a zero, you will get division by zero.
read(fstream, vector) takes an fstream and a vector. It reads a vector into the vector variable from the binary fstream. If the size of the vector variable does no match the size of the vector being a read, a CVMError is thrown.
ANCHOR Size() size()
size()
size() is a member function that will return the size, n, of the vector.
write(fstream, vector) takes an fstream and a vector. It writes the vector to the binary fstream. Size information is stored along with the vector element data.
Integer Vector Types
Integer vector types behave roughly the same as the floating point vector types; however, they are more restricted. They are derived from the integer matrix types so you may want to look at the section on those. The following functions are not available for the integer matrix types:
abs()
and the ones listed as being unavailable to integer matrix types.
Other thoughts
If you specify a zero size for a row, column, or a vector in constructors, resize, eye, or diag, the number one will be used in place of your zero. Matrix (or vector) types for a given operation must match. That means that adding a DoubleMatrix to an IntMatrix will convert the IntMatrix to a DoubleMatrix. Thus, the result of the operation will be a DoubleMatrix. Since the [] operator returns references for both the matrix classes and the vectors classes it cannot be used on temporary return values (as they are const). Therefore things like b = (a.t())[1] will not work. Instead something like:
temp = a.t();
b = temp[1];
should be used. Vectors will behave as matrices when need be since they are matrices. However, matrices are not vectors even if they only have a single row. To make them behave as vectors use the [] operator which returns a vector reference to the specified row (probably 0). As it was needed in the writing of this library and is somewhat useful a global function sign() is included. It takes a real value as an argument and returns 1 if the value is >= 0, otherwise it returns -1.
Tips and Suggestions
Many of my programs start of with:
FloatComplex i(0, 1);
That way I can use i just as you would in normal math. I normally use the type FloatComplex (as long as I am doing floating point operations) because a FloatComplex can automatically be converted to a DoubleComplex or a LongDoubleComplex (upwards only). Use inverse() instead of pow(matrix, -1). it is much faster and more stable. When doing anything that involves eigenvalue computation (such as using pow()) it is recommended that you use higher precision matrix types. The problem is that larger matrices introduce more roundoff error during the eigenvalues() function to the point where it may not converge for less precise types (namely float). On a related note when using eigenvalues() on larger matrices (even long double ones), you may find that you need to increase the maximum number of iterations, the associated tolerance, or both to make the function converge. So it may be useful to try the eigenvalues() (or pow()) function call inside a try block. That way if it fails, you can increment the tolerance or the maximum number of iterations and try again (I recommend doubling one or the other (maybe alternate between them)). The biggest problem is that even using long double matrix types, trying to call eigenvalues() or pow() on matrices larger than say 75 X 75 is basically destined to fail without ridiculous settings for tolerance and iterations. I’d love to make it better (see the feedback section and the future directions section below). I also recommend that you compile with -O3 if you are using g++ (gcc) and not using ./configure etc...
Future Directions
There are several things that are in the back of my mind as far as the future of this library is concerned. I may introduce multiple-precision (or arbitrary precision) types. This may help to alleviate the eigenvalues problem (see above). More optimization can always be done on any code. Also, as I stated in the overview, I would love to add tensor capacity. Finally, I have toyed with the idea of adding sparse matrix capabilities to the library. It would have to be done in such a manner that the user interface would remain the same, but the underlying class would decided which representation would be more efficient and call the appropriate versions of the available functions. All of these things sound great, and they would probably be a lot of fun to write. However, there are also other things I would like to work on. If I get enough feedback, I will implement more features.
Feedback
Please provide feedback. Any problems, questions, or suggestions are welcome. Email me at jasonar81@yahoo.com.
License
You are free to do whatever you want with this software. There is no warranty of any sort. It would be appreciated if improvements would be relayed back to me. You will be credited for your work.
About Me
This library was written by me, Jason Arnold. I am currently working on a Masters in Computer Science at DePaul University in Chicago. I have a Bachelors in Math and a Bachelors in Computer Science both from Northern Illinois University. My email address is jasonar81@yahoo.com.
Example
#include “cvm.h”
using std::cout;
int main()
{
FloatComplex i(0, 1); //create i
IntMatrix im(5, 5); //create a 5 X 5 matrix
im.fill(9); //fill it with 9’s
DoubleComplexMatrix dcm = DoubleComplexMatrix::eye(5); //create a 5 X 5 identity matrix
LongDoubleComplexMatrix ldcm; //create another matrix
ShortVector sv(5); //create a 5-vector
sv[0] = 1;
sv[1] = 2;
sv[2] = 3;
sv[3] = 4;
sv[4] = 5;
ldcm = (9 + i) * (sv * (im * dcm)); //ldcm will automatically resize to 1 X 5
cout << ldcm << endl;
return 0;
}