5
$\begingroup$

I have a c++ library function that calculates a distance between two vectors given an underlying matrix.

int d(const vector<float>& v1, const vector<float>& v2, const vector<vector<int>>& m) {
\\lots of stuff I don't want to understand 
}

Now the matrix m can be quite big and expensive to create. It has the same value for many evaluations of d. It only depends on an integer i that is determined once in a while during runtime.

So I would like to pass i and initialize m. Can I somehow initialize m as a c++ vector of vectors by calling c++ from Mathematica passing i and then somehow keep a handle on this c++ structure that I can pass to d(v1,v2,m)?

Or do I have to store the matrix in Mathematica itself? My fear is that it will be much bigger than the c++ equivalent and in the long run the memory usage will be a factor.

I hope the question is clear. I read many pages of documentation, but I could not figure out how to create and keep data on the c++ side in between calls. Maybe this question is similar, but I don't understand the example actually. And it has no answer.

$\endgroup$
  • $\begingroup$ "It has the same value for many evaluations". Couldn't you save space by implementing it in Mathematica with SparseArray and also setting $HistoryLength to zero. $\endgroup$ – Edmund Dec 12 '15 at 13:10
  • $\begingroup$ @Edmund Sadly the array is not sparse. However if I would take the plunge and look into the library, I probably could calculate the entries on the fly almost as fast as the lookup as it is just a Hamming distance...maybe one day, but first I want to see it run. Also, I know it but the $HistoryLength thing is always a good tip. $\endgroup$ – tortortor Dec 12 '15 at 17:32
5
$\begingroup$

This can be done without using any Mathematica features. You can keep the matrix as a global variable on the C++ side. You will have a function for initializing it, a function for destroying it, and a function for the distance calculation. Then just call the initializer and cleanup function manually from Mathematica when you need to.

Note: Storing a large matrix as std::vector< std::vector<...> > in C++ is not a good idea if you need performance. Write your own class instead which stores the matrix as a flat array and indexes into it as i*n + j or similar.

If you need very good performance, do not use MathLink. Use LibraryLink instead. With LibraryLink, you can use "constant passing" for tensors: you can pass a Mathematica matrix to C++ without making an in-memory copy of it (and have read-only access on the C++ side). See "Memory management of MTensors" in the LibraryLink documentation.

Since version 10, LibraryLink also provides "managed library expressions" (see again the LibraryLink tutorial). These make it possible to have C-side data structures which get destroyed automatically once Mathematica no longer holds references to them. This might simplify the first type of solution I mentioned where you have an initializer function and a cleanup function for the matrix. It will ensure that the cleanup function gets called automatically once there's no Mathematica side reference to the matrix.

Also take a look at my package LTemplate which makes it easier to write this sort of code with managed library expressions.

With LTemplate the solution would look something like this (sorry for not testing this, I must leave in minutes):

The template:

template = LClass["Distance",
              {LFun["setMatrix", {Integer}, "Void"],
               LFun["dist", {{Real, 1, "Constant"}, {Real, 1, "Constant"}}, Real]}
           ];

C++ code:

class Distance {
    YourMatrixType m; // may even be an mma::RealMatrixRef, i.e. an MTensor
public:
    ~Distance() { /* must clean up m if already initialized */ }

    void setMatrix(mint i) { /* initialize m */ }

    double dist(mma::RealTensorRef v1, mma::RealTensorRef v2) const {
        // compute distance between v1 and v2 based on m
    }
};

Now in Mathematica:

CompileTemplate[template]
obj = Make["Distance"]
obj@"setMatrix"[i]
obj@"dist"[v1, v2]

When there's no more references held by Mathematica to obj (e.g. you do obj =. and it's not stored in Out[...] either) then the destructor will automatically be called and the matrix m is freed.

$\endgroup$
  • $\begingroup$ The LTemplate package is great! I had to rewrite the library a bit to deal with the mma::RealTensorRef data, but now it is running and I will be able to optimize it much further if I need to. Also from now on I will be able to quickly include C++ code whenever I want to and interface with it just like with mma after loading. $\endgroup$ – tortortor Dec 13 '15 at 19:53
  • $\begingroup$ @tortortor Glad to hear that! I did see your previous comment (that you deleted), just haven't had time to respond yet ... $\endgroup$ – Szabolcs Dec 13 '15 at 22:05
  • $\begingroup$ @tortortor mma::RealTensorRef is just a thin wrapper for an MTensor. It would be a good idea to get familiar with the plain C LibraryLink API first, as this C++ class just simplifies working with those ... An MTensor stores multidimensional data as a flat array, and uses row-major storage, meaning that {{1,2},{3,4}} and {1,2,3,4} are stored the same way (plus information on their dimension). You can access this data directly. If you want, you can make an std::vector from them. std::vector has a constructor that takes a C array and a size, so no need to copy elements manually. $\endgroup$ – Szabolcs Dec 15 '15 at 11:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.