I have some code that needs to generate a large matrix based on some complicated combinations of smaller matrix elements. I find that if I write a function for the element of the larger matrix, and then compile that function, I can get the larger matrix much faster. But I get an error when I compile, and I'd like to find out the right way to do this.

This simpler code reproduces the effect.

matrix = RandomReal[{1, 2}, {25, 25}];
tupleslist = RandomInteger[{1, 25}, {1000000, 2}];

matsubtractA[i_Integer, j_Integer] := matrix[[i, j]] - matrix[[j, i]];

Total[matsubtractA @@@ tupleslist] // AbsoluteTiming

matsubtractB = Compile[{{i, _Integer}, {j, _Integer}},
                       matrix[[i, j]] - matrix[[j, i]]

Total[matsubtractB @@@ tupleslist] // AbsoluteTiming

matsubtractC = Compile[{{i, _Integer}, {j, _Integer}},
                               matrix[[i, j]] - matrix[[j, i]]

Total[matsubtractC @@@ tupleslist] // AbsoluteTiming

The result is

enter image description here

All three methods give the same answer, but with much different execution times. The first attempt at Compile is apparently worthless, since it increases the time. The second attempt, where I use Evaluate within compile, greatly improves the execution time but gives a Part::pkspec1 error when compiling: "The expression i cannot be used as a part specification."

In my actual code, the speedup is even more drastic, so I'll use it even with the error. But I'd love to know how to fix it. I even tried using the mysterious third argument to Compile,

matsubtractC2 = Compile[{{i, _Integer}, {j, _Integer}},
            mat = matrix;
            mat[[i, j]] - mat[[j, i]]
      {{mat, _Real, 2}}];

Total[matsubtractC @@@ tupleslist] // AbsoluteTiming

But I got the same error.

  • $\begingroup$ What happens if you do With[{mat = matrix}, matsubtract = Compile[{{i, _Integer}, {j, _Integer}}, mat[[i, j]] - mat[[j, i]]]]? $\endgroup$ – J. M.'s technical difficulties Jul 15 '15 at 12:24
  • $\begingroup$ @Guesswhoitis., if I do that then it solves it completely. Post as an answer and I would accept it so you can have the imaginary internet points. Also, can you explain why this works? $\endgroup$ – Jason B. Jul 15 '15 at 12:26
  • $\begingroup$ I'll let somebody (you?) write the answer instead, but let me sketch out what happened there: With[] "injected" your huge matrix inside the compiled function, so Part[] will no longer have anything to complain about. $\endgroup$ – J. M.'s technical difficulties Jul 15 '15 at 13:17

The problem here is more simple: it's the same error you'd get if you tried matrix[[i, j]]. You told Mathematica to Evaluate the expression matrix[[i,j]] - matrix[[j,i]] before doing the Compile, so it reduces to that problem. The problem is that the symbol i must be an integer to be a valid part specification: Part[{1, 2, 3}, i] throws an error.

Of course, Mathematica keeps the expression unevaluated, so you can subsequently substitute i -> 1 to get the answer 1. You can prevent the error from being thrown using Quiet.

|improve this answer|||||
  • $\begingroup$ Why is it significantly faster when I use Evaluate than when I don't? $\endgroup$ – Jason B. Jul 15 '15 at 13:13
  • $\begingroup$ @JasonB The form with Evaluate, or With[{mat = matrix}, is putting the entire matrix inside the compiled function rather than referencing through an external call. $\endgroup$ – Mr.Wizard Jul 15 '15 at 13:16
  • 1
    $\begingroup$ You can find this out using the CompiledFunctionTools package's function CompilePrint. Using Evaluate, you let Compile know what matrix is in advance; without Evaluate, the compiled function has to check the value of matrix with the Mathematica kernel each time it executes, and that's slow. $\endgroup$ – Patrick Stevens Jul 15 '15 at 13:17

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.