C and C++¶
Here are some tips for those who wish to use the compiled programming language C or C++ for scientific work instead of some interpreted programming language (Matlab, ...). I assume, that you have some understanding of the fundamentals of the syntax of C and C++ but you are looking for high level functionality such as plotting, random number generation and handling of matrices.
I recommend two styles:
- If you are using plain C, then I suggest to use the GSL (Gnu Scientific Library).
- If you are using C++, then I suggest to use the boost library.
I explain my ideas by giving examples.
Visualization¶
Since most scientific writing is done in LaTeX
I suggest to visualize data
as follows. In the program, the data is written to a text file. Then we use
the LaTeX
package pgfplots
to plot this data. (See my examples in the Graphics in LaTeX with PGF section.)
Consider the following three files:
The C style file
viz.c
:#include <stdio.h> #include <math.h> int main(int argc, char **argv) { unsigned int N = 30, n; float Ts = 0.05, f = 1.7, t, x, y; FILE *fp; char fname[] = "viz.dat"; fp = fopen(fname,"w"); if (fp == NULL) { fprintf(stderr, "Error: Failed to open file " "\"%s\" in write mode.\n", fname); return -1; } fprintf(fp, " t\t sin\t exp\n"); for(n = 0; n < N; n = n++) { t = n*Ts; x = cos(t*f*2*M_PI); y = exp(-t); fprintf(fp, "%1.5f\t%+1.5f\t%+1.5f\n", t, x, y); } fclose(fp); printf("done\n"); return 0; }
The C++ style file
viz.cpp
:#include <iostream> #include <fstream> #include <cmath> int main(int argc, char **argv) { unsigned int N = 30, n; float Ts = 0.05, f = 1.7, t, x, y; char fname[] = "viz.dat"; std::ofstream file(fname); if (! file.is_open()) { std::cerr << "Error: Failed to open file \"" << fname << "\" in write mode." << std::endl; return -1; } file << std::scientific; file << "t\t" << "sin\t" << "exp" << std::endl; for(n = 0; n < N; n = n++) { t = n*Ts; x = cos(t*f*2*M_PI); y = exp(-t); file << t << "\t" << x << "\t" << y << std::endl; } file.close(); std::cout << "done" << std::endl; return 0; }
Finally the
LaTeX
fileviz.tex
:\documentclass{article} \usepackage{pgfplots} \begin{document} \begin{tikzpicture} \begin{axis} \addplot table[x=t,y=sin]{viz.dat}; \addplot table[x=t,y=exp]{viz.dat}; \legend{Sinusoid,Exponential} \end{axis} \end{tikzpicture} \end{document}
We can compile the C file or the C++ file with the GNU compiler. The resulting program produces our data file
viz.dat
which we want to plot. Finally, we use LaTeX
to produce a pdf
file containing the plot.
The sequence of commands for the C style file is:
gcc viz.c -lm
./a.out
pdflatex viz.tex
The sequence of commands for the C++ style file is:
g++ viz.cpp
./a.out
pdflatex viz.tex
The plot should look as follows:
Random Numbers¶
In plain C, GSL provides good random number generators. The following example
uses the default generator: (Compile with gcc random.c -lgsl -lgslcblas
.)
#include <stdio.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
const gsl_rng_type *T;
gsl_rng *r;
int main (void) {
/* Here, we initialize the random number generator. */
gsl_rng_env_setup();
T = gsl_rng_default;
r = gsl_rng_alloc(T);
unsigned int N = 50, n;
float Ts = 0.01, f = 5.0, t, x, y;
FILE *fp;
char fname[] = "random.dat";
fp = fopen(fname, "w");
if (fp == NULL) {
fprintf(stderr, "Error: Failed to open file "
"\"%s\" in write mode.\n", fname);
return -1;
}
fprintf(fp, " t\t sin\t noisy\n");
for(n = 0; n < N; n = n++) {
t = n*Ts;
x = cos(t*f*2*M_PI);
y = x + gsl_ran_gaussian(r, 0.1);
fprintf(fp, "%1.5f\t%+1.5f\t%+1.5f\n", t, x, y);
}
fclose(fp);
gsl_rng_free(r);
printf("done\n");
return 0;
}
In C++, boost provides good random number generators. The following example
uses a Mersenne Twister generator. (Compile with g++ random.cpp
.)
#include <iostream>
#include <fstream>
#include <boost/random.hpp>
namespace bst = boost;
// Here, we construct and initialize the random number generator.
static bst::mt19937 rng;
static bst::normal_distribution<double> norm_dist(0, 0.1);
static bst::variate_generator<bst::mt19937&,
bst::normal_distribution<double> > randn(rng, norm_dist);
int main(int argc, char **argv) {
unsigned int N = 50;
float Ts = 0.01, f = 5.0, t, x, y;
char fname[] = "random.dat";
std::ofstream file(fname);
if (! file.is_open()) {
std::cerr << "Error: Failed to open file \"" << fname
<< "\" in write mode." << std::endl;
return -1;
}
file << std::scientific;
file << "t\t" << "sin\t" << "noisy" << std::endl;
for(int n = 0; n < N; n = n++) {
t = n*Ts;
x = cos(t*f*2*M_PI);
y = x + randn();
file << t << "\t" << x << "\t" << y << std::endl;
}
file.close();
std::cout << "done" << std::endl;
return 0;
}
Note
Starting from boost version 1.47, the random number part of the boost
library has been moved to the namespace boost/random
and header
files are split up in several files. The first lines of this example
have to be changed to
#include <iostream>
#include <fstream>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/random/normal_distribution.hpp>
using namespace std;
using namespace boost::random;
The resulting data should look as follows:
Matrix Inversion¶
In plain C, GSL provides structures for matrices and vectors. A general
non-singular matrix can be inverted using the LU-factorization as shown in the
following example matrix-inverse.c
. (Compile with gcc
matrix-inverse.c -lgsl -lgslcblas
.)
#include <stdio.h>
#include <gsl/gsl_math.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_matrix.h>
#include <gsl/gsl_linalg.h>
#include <gsl/gsl_blas.h>
const gsl_rng_type *T;
gsl_rng *r;
/* matrix inversion routing using LU-factorization */
int invert_matrix(const gsl_matrix *A, gsl_matrix *Ai)
{
/* check matrix sizes */
if(! A->size1 == A->size2
&& Ai->size1 == Ai->size2
&& A->size1 == Ai->size1)
return (-1);
/* create a permutation matrix for the LU-factorization */
gsl_permutation * P = gsl_permutation_alloc(A->size1);
int s; /* a dummy variable */
/* allocate a buffer matrix */
gsl_matrix *AA = gsl_matrix_alloc(A->size1, A->size2);
gsl_matrix_memcpy(AA, A); /* copy A to AA */
/* perform LU-factorization */
gsl_linalg_LU_decomp(AA, P, &s);
/* backsubstitute to get the inverse */
gsl_linalg_LU_invert(AA, P, Ai);
return 0;
}
int main (void)
{
/* initialize the random number generator */
gsl_rng_env_setup();
T = gsl_rng_default;
r = gsl_rng_alloc(T);
/* generate a random matrix */
gsl_matrix * A = gsl_matrix_alloc(3,3),
* B = gsl_matrix_alloc(3,3),
* Ai = gsl_matrix_alloc(3,3);
int n, m;
for (n = 0; n < 3; n++)
for (m = 0; m < 3; m++)
gsl_matrix_set(A,n,m,gsl_ran_gaussian(r, 0.1));
printf("A =\n");
gsl_matrix_fprintf(stdout,A,"%g");
invert_matrix(A,Ai);
/* compute B = A*Ai */
gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, A, Ai, 0.0, B);
printf("\nA*Ai=\n");
gsl_matrix_fprintf(stdout,B,"%g");
/* compute B = Ai*A */
gsl_blas_dgemm(CblasNoTrans, CblasNoTrans, 1.0, Ai, A, 0.0, B);
printf("\nAi*A=\n");
gsl_matrix_fprintf(stdout,B,"%g");
/* free all the allocated objects */
gsl_matrix_free(A);
gsl_matrix_free(B);
gsl_matrix_free(Ai);
gsl_rng_free(r);
printf("\ndone\n");
return 0;
}
In C++, we use again boost
and boost::ublas
to perform the same matrix
inversion based on the LU-factorization as shown in the following example
matrix-inverse.cpp
. (Compile with g++ matrix-inverse.cpp
.)
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/lu.hpp>
#include <boost/numeric/ublas/io.hpp>
#include <boost/random.hpp>
namespace bst = boost;
namespace ubl = boost::numeric::ublas;
static bst::mt19937 rng;
static bst::normal_distribution<double> norm_dist(0,1);
static bst::variate_generator<bst::mt19937&,
bst::normal_distribution<double> > randn(rng, norm_dist);
// matrix inversion routine using lu_factorize and lu_substitute
template<class T>
bool invert_matrix(ubl::matrix<T> A, ubl::matrix<T>& Ai)
{
// create a permutation matrix for the LU-factorization
ubl::permutation_matrix<std::size_t> P(A.size1());
// perform LU-factorization, this will change A
if(ubl::lu_factorize(A, P))
return false;
// create identity matrix
Ai.assign(ubl::identity_matrix<T> (A.size1()));
// backsubstitute to get the inverse
ubl::lu_substitute(A, P, Ai);
return true;
}
int main(int argc, char **argv)
{
ubl::matrix<double> A(3, 3), Ai(3, 3);
// generate a random matrix
for(int i=0; i<3; i++)
for(int j=0; j<3; j++)
A(i,j) = randn();
std::cout << "A = " << A << std::endl;
if(invert_matrix(A, Ai))
{
std::cout << "Ai = " << Ai << std::endl;
std::cout << "Ai*A = " << prod(Ai,A) << std::endl;
std::cout << "A*Ai = " << prod(A,Ai) << std::endl;
}
else
std::cout << "A is singular" << std::endl;
return 0;
}
An example output is, e.g.:
A = [3,3]((0.357628,0.14061,1.39772),(0.0460402,-1.46791,-0.770404),(0.274781,-0.892802,1.63291))
Ai = [3,3]((4.84101,2.31866,-3.04983),(0.450192,-0.313718,-0.533363),(-0.568484,-0.561703,0.833999))
Ai*A = [3,3]((1,8.88178e-16,-1.77636e-15),(0,1,-1.11022e-16),(-2.77556e-17,-1.11022e-16,1))
A*Ai = [3,3]((1,0,0),(0,1,-1.11022e-16),(-3.33067e-16,0,1))