2

I have a symbolic vector with about 60,000 short symbolic expressions, one in each cell. Such an expression would typically look like:

(81*A_1_1*W1)/(646*L) - (81*A_6_6*L)/(646*W1)

There are about 20 distinct variables in total appearing in these expressions, of which I know the values.

The problem centers around evaluating this symbolic matrix as efficiently as possible. I will need to do this 1000s of times in future (this computation is part of the objective function in an optimization problem).

I wrote a Matlab function that looks like:

function [K_1_uniques]=K_1_computation(C1,W1,L)

%Define variables from elasticity tensor C1
A_1_1=C1(1,1);
A_1_2=C1(1,2);
A_1_6=C1(1,3);
A_2_2=C1(2,2);
A_2_6=C1(2,3);
A_6_6=C1(3,3);
D_1_1=C1(4,4);
D_1_2=C1(4,5);
D_1_6=C1(4,6);
D_2_2=C1(5,5);
D_2_6=C1(5,6);
D_6_6=C1(6,6);
A_4_4=C1(7,7);
A_4_5=C1(7,8);
A_5_5=C1(8,8);

%Initialize vector
K_1_uniques=zeros(65251,1);

%Populate vector
K_1_uniques(1)=(81*A_1_1*W1)/(646*L) - (81*A_6_6*L)/(646*W1);
K_1_uniques(2)=(63*A_4_5*L)/1427660 - (27*A_5_5*W1)/1427660;
K_1_uniques(3)=(63*A_4_4*L)/1427660 - (27*A_4_5*W1)/1427660;
...
K_1_uniques(65251)=- (2187*A_4_4*L)/62817040 - (2187*A_4_5*W1)/102077690;

This takes MATLAB about 12.3 seconds to run (timed with tic-toc) the first time, subsequent times it takes 0.030158 seconds to run because I guess it retains the compilation? Am I right in assuming that Mex pre-compilation will eliminate this long first run (IMO due to JIT).

So I am wondering if I can further speed up this computation by running it as a Mex file? I don't have the coder package, so I'm having to write the c code by hand (well, at least the syntax, I will evidently use Matlab's fprintf to write the 65,000 expressions).

It's my first time writing C Mex code (I have some basic C experience). I have managed to run a "hello world" and some basic integer and double arithmetic so I have an idea of what is going on.

If I understand correctly, the Mex files are pre-compiled C code so should save some time compared to the JIT compilation in Matlab.

I would like to input to the Mex file an 8*8 double matrix (the elasticity tensor, C1) as well as two double values (scalars), W1 and L.

The program should then assign A_1_1=C1(1,1), A_1_2=C1(1,2)... as in the Matlab code. It should then populate a new matrix array that has 65,521 entries which it outputs.

I have a simple outline of such a code below (for just 5 entries), the syntax is not all correct, but it's a start. It compiles without errors but crashes Matlab if I try to run it. I've assigned A_1_1 and A_1_2 as scalar inputs because I'm not sure how I would extract these from an input matrix C1. I'm also not sure of how to write to a matrixArray so I've used a cellArray instead (this should be changed).

#include "mex.h" /* Always include this */
void mexFunction(double nlhs, mxArray *plhs[], /* Output variables */
                double nrhs, mxArray *prhs[]) /* Input variables */
{
#define N 5
double A_1_1=*mxGetPr(prhs[0]);
double A_6_6=*mxGetPr(prhs[1]);

plhs[0]=mxCreateCellArray(1, N);
mxSetCell(plhs[0], 1, 2*A_1_1/84 + 3*A_6_6/92);
mxSetCell(plhs[0], 2, 5*A_1_1/120 + 7*A_6_6/11);
mxSetCell(plhs[0], 3, 2*A_1_1/56 + 4*A_6_6/82);
mxSetCell(plhs[0], 4, 12*A_1_1/7 + 3*A_6_6/48);
mxSetCell(plhs[0], 5, 2*A_1_1/74 + 8*A_6_6/92);

return;
}

Any help in getting this code to run would be much appreciated. Also, I don't know any of the C tricks regarding memory allocation etc. that could further accelerate this procedure. Any advice or comments in this regard, as well as what I'm attempting in a broader sense, would be highly appreciated.

Kobs
  • 209
  • 3
  • 8
  • 1
    Can you be more clear about what you mean by "symbolic vector" and "symbolic expressions"? Are these just strings that you're evaluating in double precision. Or are you performing actual symbolic math where you obtain an exact answer in terms of the symbolic variables. If it's not the latter, then I'd refrain from referring to this as "symbolic" to avoid confusion. Since you haven't even optimized you Matlab code, you shouldn't be worrying about C code yet: pre-mature optimization. Also, the long first run may also be due memory allocation or poor coding. – horchler Dec 15 '13 at 16:06
  • I assume `K_1_uniques` has many blocks of very similar lines. Try to vectorize these. – Daniel Dec 15 '13 at 17:08
  • In Matlab, the class of this object is "symbolic", they are symbolic mathematical expressions that are manipulated using Matlab's symbolic engine muPad. – Kobs Dec 15 '13 at 18:51
  • I assume that you're timing after the Symbolic Math toolbox has been initialized first? Even just running `syms x` on a fresh launch of Matlab takes several seconds because the the entire `sym` class needs to be instantiated and it's big. And if all of your variable are class `sym`, then I think that it may make more sense to preallocate like this: `K_1_uniques = sym(zeros(65251,1);)`. Otherwise your output isn't even symbolic (and you should get an error if any inputs are symbolic variables as opposed to symbolic values). – horchler Dec 15 '13 at 19:53

1 Answers1

2

I tend to trust this answer to your previous question. Even the answerer of the accepted answer agreed that it was correct. You might read through that again.

One thing that your code could clearly benefit from is common subexpression elimination. This question/answer suggests that Matlab's JIT does not perform this, though the version of Matlab is not indicated. For your code this will take a lot of tedious work.

As you're using the Symbolic Math toolbox, the subexpr function may be helpful for this. I'm not sure how well this will work as the Symbolic toolbox can bog down on large inputs even on fast machines with plenty of memory so you might need to break the problem up. Of course find and replace can also be used as long as you're careful. With the latter method you can first look for and eliminate small common subexpression and then combine these. Matlab's Editor has trouble with large files because it's trying to colorize it and check the code every time something changes so you might also switch to a full-featured text editor that has advanced search and replace features for this. Moving to mex should also eliminate many common subexpressions as long as you compile with the standard optimization level. But at that point it would make more sense to implant this as more standard loops.

Community
  • 1
  • 1
horchler
  • 18,384
  • 4
  • 37
  • 73
  • If I could kindly ask you to not base your response here on my previous question, the questions are distinct. You state a lot of things about my code not being optimized - this is NOT the same code as in my previous question. In the meantime, it has been extremely streamlined. Let me demonstrate; the 65,000 lines here are actually the unique values in a 500*500 matrix. I would be interested in hearing how you think I can further optimize the few snippets of code I've posted here? I think you'd be hard pressed. – Kobs Dec 15 '13 at 18:59
  • @Kobye: My answer *is* based on the code snippets you posted for this question and *can* be used to optimize it unless you don't happen to have single common subexpression among your 65,000+ lines. And now that clarified that these are actual symbolic variables and expressions already, `subexpr` may make even more sense. My first paragraph refers to your comments about JIT compilation which very likely is not specific to one set of code. – horchler Dec 15 '13 at 19:33