A brief discussion of the context and future direction for an object-oriented scientific computing library.
For example, function objects can now be defined and manipulated very succinctly:
double a=-0.9, b=0.9; o2scl::root_brent_gsl<> solver; std::function<double(double)> f=[](double x){ return sin(x)-0.1; }; solver.solve(a,b,f); cout << a << endl;(adapted from O_{2}scl's ex_lambda.cpp example)
C-style scientific libraries like GSL accept functions based on function pointers, which means that the code inside the function is not known at compile-time. For C++ classes like o2scl::root_brent_gsl above, because the function type is a template parameter, the C++ compiler can know which function is being solved at compile time, which allows for more optimizations.
O_{2}scl has been successfully used on all of NERSC's largest machines. The next version of will have a configure flag, --disable-cpp11, which ensures compilation with earlier compilers and will use the Boost configuration macros to ensure C++11 is used only when it is supported.
Among these options are uBlas, Eigen, Armadillo, and more. However, none of them seem to agree on the basics of how these objects should be written. For example, consider the basic reallocation method, resize(). In the C++11 standard, this is written
void std::vector::resize(size_type n)
and it is assumed that the action of resize() is destructive. On the other hand, in uBlas, resizes are non-destructive by default
void ublas::vector::resize(size_type n, bool preserve=true)
Armadillo vectors follow the same behavior of std::vector for destructive resizes, but introduce a new notation for non-destructive ones
void arma::row::set_size(size_t n)Eigen operates similarly, but uses the name conservativeResize() for non-destructive resizes. O_{2}scl, for the time being, presumes that all resizes are destructive, and uses a notation similar to the C++11 standard for its resize methods.
(Eventually, a set of linear algebra type traits is necessary to describe how linear algebra types behave and how they interact with each other. )
Obtaining rows and columns of matrices (without a copy) is also not trivial. In uBlas, the row of a matrix is obtained by using the constructor of a matrix_row object. In Armadillo and Eigen, this is performed by using the .row() method, resulting in a vector object of an unusual type. O_{2}scl subsumes some of this complexity into a function o2scl::matrix_row, which can be used with any of these linear algebra libraries
// With uBlas typedef boost::numeric::ublas::matrix<double> matrix; typedef boost::numeric::ublas::matrix_row<ubmatrix> matrix_row; matrix_row r1=o2scl::matrix_row<matrix,matrix_row>(m1,2); // With Eigen Eigen::MatrixXd::RowXpr r2= o2scl::matrix_row<Eigen::MatrixXd,Eigen::MatrixXd::RowXpr>(m2,2); // With Armadillo arma::subview_row<double> m1= o2scl::matrix_row<arma::mat,arma::subview_row<double> >(m3,2);
Heterogeneous architectures also naturally lead to more data types to help contain and manipulate data on them. The issues described above will likely become more commonplace.
At the same time, C++ offers quite a bit of syntactic simplicitly, with relatively little cost in speed. Even though linear algebra libraries do not agree on the details of the interface, they can speed up development time considerably.
GSL does, of course, but is more difficult to use with C++ member functions. Two-dimensional interpolation has only recently found its way into the GSL repository yet has been in O_{2}scl for many years. In fact, some GSL improvements were guided by O_{2}scl development.
O_{2}scl is still in beta, so this is a perfect time to let me know if it is useful to you (or not), and feel free to make comments or suggestions.