1
$\begingroup$

Suppose I have such a module as below:

test[int_] := Module[{m},
  m = Table[i, {i, 1, 10}];
  Do[m[[i]] = i*i, {i, 1, int}];
  Return[m]
  ]

The bound for Do loop is not determined until an argument is given to the test. The argument must be between 1 and 10 in this mini example. It works fine:

test[10]
(*{1, 4, 9, 16, 25, 36, 49, 64, 81, 100}*)

Now, I want to compile it:

test2 = Compile[{{iter, _Integer}}, test[iter], 
  "RuntimeOptions" -> {"EvaluateSymbolically" -> False}, 
  CompilationOptions -> {"InlineCompiledFunctions" -> True}, 
  Parallelization -> True]

executing this returns:

test2[10]
CompiledFunction::cfex: Could not complete external evaluation at instruction 1; proceeding with uncompiled evaluation. >>
{1, 4, 9, 16, 25, 36, 49, 64, 81, 100}

So, it evaluates test2 uncompiled.

Then, I tried to use Evaluate:

test3 = Compile[{{iter, _Integer}}, Evaluate[test[iter]], 
  "RuntimeOptions" -> {"EvaluateSymbolically" -> False}, 
  CompilationOptions -> {"InlineCompiledFunctions" -> True}, 
  Parallelization -> True]

The compiling returns:

Do::iterb: Iterator {i,1,iter} does not have appropriate bounds. >>

and if I run compiled version using Evaluate it returns:

test3[10]
(*{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}*)

It won't execute the Do loop.

How can I compile a Module which has a loop with the upper bound as a variable?

Edit

I changed the code to see how the answer below works:

test[int_] := Module[{m, n},
  m = Table[i, {i, 1, int}];
  n = Table[i*j, {i, 1, int}, {j, 1, int}];
  Do[m[[i]] = Tr[n.n], {i, 1, int}];
  Return[m]]

test3 = Compile @@ (Hold[{{iter, _Integer}}, test[iter]] /. 
    DownValues@test)

test[200]; // AbsoluteTiming
(*{0.104010, Null}*)

test3[200]; // AbsoluteTiming
(*{6.565657, Null}*)

Compiled version is much more slow.

$\endgroup$
2
  • $\begingroup$ Tr is not compiled, but results in a call to MainEvaluate. $\endgroup$
    – Michael E2
    Commented Dec 19, 2014 at 17:17
  • $\begingroup$ OK. I just to put it there to make the calculations longer so that I can use AbsoluteTiming. $\endgroup$
    – MOON
    Commented Dec 19, 2014 at 22:24

2 Answers 2

3
$\begingroup$
test3 = Compile @@ (Hold[{{iter, _Integer}}, test[iter]] /. DownValues@test)
$\endgroup$
2
  • $\begingroup$ Could you please check my edited question? $\endgroup$
    – MOON
    Commented Dec 19, 2014 at 15:36
  • $\begingroup$ @yashar As mentioned by Michael E2 above, that's mainly because Tr isn't compiled. Something like test[int_] := Module[{m}, m = Table[i, {i, 1, 2 int}]; Do[m[[i]] = i*i, {i, 1, int}]; Return[m]] and test3[10^4]; // AbsoluteTiming is enough to show the difference. $\endgroup$
    – xzczd
    Commented Dec 22, 2014 at 3:50
0
$\begingroup$

Your first example could be just as easily achieved with Table[i*i, {i, 1, iter}], which compiles.

test1[int_] := Module[{m},
  m = Table[i, {i, 1, int}];
  Do[m[[i]] = i*i, {i, 1, int}];
  Return[m]
]

test2[int_] := Table[i*i, {i, 1, int}];

test3 = Compile[{{iter, _Integer}}, Table[i*i, {i, 1, iter}], 
   CompilationTarget -> "C"];

Note how I've modified your function, test1, because m needs to be same size as int, which in your case is only true for int = 10. Now time them...

Do[test1[1000], {1000}] // AbsoluteTiming
(* 0.98 seconds *)

Do[test2[1000], {1000}] // AbsoluteTiming
(* 0.015 seconds *)

Do[test3[1000], {1000}] // AbsoluteTiming
(* 0.006 seconds *)

Looking at your second example, by considering what the Do loop is doing...

test1[int_] := Module[{m, n}, m = Table[i, {i, 1, int}];
  n = Table[i*j, {i, 1, int}, {j, 1, int}];
  Do[m[[i]] = Tr[n.n], {i, 1, int}];
  Return[m]]

test2[int_] := Module[{n},
  n = Table[i*j, {i, 1, int}, {j, 1, int}];
  ConstantArray[Tr[n.n], {int}]]

test3[int_] := Module[{n},
  n = Outer[Times, Range@int, Range@int];
  ConstantArray[Tr[n.n], {int}]]

...a significant speed-up can be achieved.

Needs["GeneralUtilities`"]
test1[200]; // AccurateTiming
(* 0.059 seconds *)

test2[200]; // AccurateTiming
(* 0.00098 seconds *)

test3[200]; // AccurateTiming
(* 0.00038 seconds *)

I presume that the example codes are different to your actual problem. Given the performance of the Do loop shown here, the question you need to ask yourself is "Is a Do loop the best way to go for my problem?"

$\endgroup$
2
  • $\begingroup$ I need Do loop. At each iteration there are some step by step calculations. Each step gives the input for the next step. The code I brought here are intended to show my problem . My actual code is more complicated than that. $\endgroup$
    – MOON
    Commented Dec 19, 2014 at 16:39
  • $\begingroup$ "Each step gives the input for the next step.", in which case, have you looked at Nest or something similar? reference.wolfram.com/language/ref/Nest.html, which is compilable... $\endgroup$ Commented Dec 19, 2014 at 16:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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