-
Notifications
You must be signed in to change notification settings - Fork 0
GSoC 2015 Abinash Meher: Ruby bindings for CSymPy
The motivation was to have a computer algebra system for Ruby. At the beginning, the idea was to use ruby wrapper for sage which uses Pynac, based on GiNaC, because it is much faster.
However, from the benchmarks, CSymPy is much faster than Pynac. The reason Pynac is slower is probably because it uses Sage's GMP functionality. In case of CSymPy there is no Python being called from the C++ code. The code is entirely in C++. It was for this reason that there's no need to worry about any potential Python overhead. It also allows us to use it from other languages, with the help of wrappers.
Another option was to wrap GiNaC directly, but CSymPy also seems to be a bit faster than GiNaC itself by now. The code has been designed in such a way so that one can play with various data structures and really make sure the code is fast.
There is one small limitation however. Compared to GiNaC, CSymPy is missing specialized polynomial
manipulation and series expansion and pattern matching, all of which are being worked on and will get the functionality soon. Most part of it might get completed during this summer as part of GSoC projects.
Execution
-
Tool to generate the wrappers
There were many choices like Ruby inline, Rice, FFI, SWIG and manually using Ruby C API . From the first 3 FFI seems to be the fastest as benchmarked in this link. However, with the FFI method, we get a segfault at runtime while running Ruby tests (which we will be adding whatever tool we use). Here manual method is advantageous since it is compiled, so we immediately get a compile error on Travis if we change an interface in CSymPy, so we know we have to fix it, while merging the patch. It will be a huge comfort. Also, the manual method is preferred while dealing with a lot of pointers in the C++ code. Because either way we would end up doing as much work. Also the code is clearer in the manual method. With SWIG, C++ references are supported, but SWIG transforms them back into pointers, as mentioned here. It's a feature that we might be needing some time later. So, going with the manual method seems the wisest. -
File structure
Currently all the python wrappers are in a foldercsympy
under the root folder. The idea is to keep all the wrapper code at a single place, i.e. inside thesrc
in separate folders likesrc/c
,src/python
andsrc/ruby
. The same logic can be applied to other languages later likesrc/julia
, etc.
Each folder can then be configured to a ready to install package like the python wrappers as a pip-package and the ruby wrappers as a gem. -
Exposing the C++ functions to C with
extern "C"
Ruby provides interfacing to only C functions. For that we need to expose the C++ code throughextern
. The functions can now be called from C. First we need a C++ class, using one header file (Test.hpp)
//File: Test.hpp
class Test {
public:
void testfunc();
Test(int i);
private:
int testint;
};
and one implementation file (Test.cpp)
//File: Test.cpp
#include <iostream>
#include "Test.hpp"
using namespace std;
Test::Test(int i) {
this->testint = i;
}
void Test::testfunc() {
cout << "test " << this->testint << endl;
}
This is just basic C++ code.
Then we need some glue code. This code is something in-between C and C++. Again, we got one header file (TestWrapper.h, just .h as it doesn't contain any C++ code)
//File: TestWrapper.h
typedef void CTest;
#ifdef __cplusplus
extern "C" {
#endif
CTest * test_new(int i);
void test_testfunc(const CTest *t);
void test_delete(CTest *t);
#ifdef __cplusplus
}
#endif
and the function implementations (TestWrapper.cc, .cc as it contains C++ code):
//File: TestWrapper.cc
#include "TestWrapper.h"
#include "Test.hh"
extern "C" {
CTest * test_new(int i) {
Test *t = new Test(i);
return (CTest *)t;
}
void test_testfunc(const CTest *test) {
Test *t = (Test *)test;
t->testfunc();
}
void test_delete(CTest *test) {
Test *t = (Test *)test;
delete t;
}
}
-
Writing the extensions
I will be following the documentation for this from README.EXT. Also the Chris Lalance's blog. -
Data structures in CSymPy
std::vector
andstd::map
-
Making this a gem
So that it's easier to install. User will have the choice if he wants to install along with csympy or only the wrappers. A check can be included to automate this. Also a functionality to compile the extensions separately.
I am using a system dual-booted with Ubuntu 14.04.2 LTS and Windows 8.1. Following are the configurations on my machine
abinashmeher999@JARVIS:~$ ruby --version
ruby 2.0.0p481 (2014-05-08 revision 45883) [x86_64-linux]
abinashmeher999@JARVIS:~$ gem --version
1.8.23
abinashmeher999@JARVIS:~$ rake --version
rake, version 10.0.4
abinashmeher999@JARVIS:~$ rspec --version
3.2.2
abinashmeher999@JARVIS:~$ bundle --version
Bundler version 1.3.5
abinashmeher999@JARVIS:~$ rdoc --version
rdoc 3.9.5
I am using RVM to manage the ruby versions in my system. Besides that, I will be using vim as my primary text editor. Apart from that I will be using the following
-
Rake-compiler
rake-compiler is a set of rake tasks for automating extension building. Rake eases the process of making extensions by its'rake/extensiontask'
. If a proper project structure is followed, generating extensions requires only a few lines of code. -
RSpec
RSpec tests it the way a developer would like it to, to make sure all works as he intended them. More like the unit tests. Whereas, Cucumber tests it the way a client/consumer would expect from the software. Like the integration tests. Most of the places, people suggest that both go hand in hand. But since the underlying C++ code is tested elsewhere, integration testing won't be needed. -
Bundler
Bundler provides a consistent environment for Ruby projects by tracking and installing the exact gems and versions that are needed. It ensures that the gems you need are present in development, staging, and production. -
RDoc
For documentation of code and tests.
I don't know everybody yet, neither did I get enough time to know at least a few developers in the community before the application. This will be a great time to get to know everybody and the fellow students. My summer vacation will start from 29th of April and I can get up to speed by reading the documentation and getting to know the practices followed in the community. I will also use this time to read the documentation for the tools I will be using, so that I am aware of the best way to achieve the result and make informed decisions.
- Writing the C wrappers with
extern
.
- Writing the source code for the wrappers.
- Documenting it on the go.
- Reviewing what was done in week 4, 5, 6 and 7
- Writing and carrying out tests using RSpec
- Might need to go back to make some changes to accommodate the coding style in ruby
- Packaging the code
- Make it ready for publication
- cleaning up the code, fixing bugs, documentation
- addition of more tests, examples and everything that's pending
- Make sure the installation works on all systems