6
$\begingroup$

What did I do before?

I seem not to be able to understand MMA's evaluation scheme. I read some posts like Preventing Evaluation or a MathGroup post where the topics of evaluation prevention are discussed. Unfortunately, I am not able to fully understand it and use it for my specific purpose. Direct help (or hints to other useful posts) are appreciated.

What's the problem?

I use a quite complicated (and messy) function generate to generate further functions. This function could output

2 ev[c, k, kF, LatticeSize]

for example. The evaluation to find the new function is costly and I don't want to repeat it but afterwards directly work with the above evaluation result, so I use (notice the Set notation instead of SetDelayed)

(* "…" symbolize parameters to generate that are not of importance here for understanding the problem. *)
fun[c_,k_] = generate[…] 

Now it is possible to evaluate fun[5,6,1.5,4] for example and get a result. As soon as I define ev to be a compiled function (performance is of interest for me), the generate function tries to evaluate ev directly. How can this be prevented and instead fun be made a function that is directly dependent on the compiled (and not evaluated function) ev?

Bare minimum code

generate[] := 2 ev[c, k, kF, LatticeSize];
ev = Compile[{{i, _Integer}, {j, _Integer}, {kF, _Real},{LatticeSize, _Integer}}, If[i == j, kF/\[Pi], With[{dist = Min[Abs[i - j], LatticeSize - Abs[i - j]]}, 1/\[Pi]*Sin[kF dist]/dist]], RuntimeAttributes -> {Listable}, Parallelization -> True, RuntimeOptions -> "Speed", CompilationTarget -> "C"];

Evaluation of

fun[c_, k_, kF_, LatticeSize_] = generate[]

should show the problem now.

$\endgroup$
3
  • 2
    $\begingroup$ Could it be an option to temporarily unset ev while you define fun? Block[{ev}, fun[...] = generate[]]. The fun definition would not contain the compiled functions in this case, only the ev symbol. This should not be a problem for as long as you don't change the value of ev. $\endgroup$
    – Szabolcs
    Commented Feb 16, 2017 at 13:41
  • $\begingroup$ @Szabolcs Embarrassing I didn't see the solution myself. Many thanks, it - of course - works like a charm. If you want to answer the question in a full post and not a comment, I will accept it, making clear for others the question is resolved. $\endgroup$
    – pbx
    Commented Feb 16, 2017 at 13:51
  • 1
    $\begingroup$ I'm not sure why the close vote. This doesn't seem to be a trivial mistake, and if it's found easily in the documentation, then I have been reading the wrong docs :-) $\endgroup$
    – MarcoB
    Commented Feb 16, 2017 at 14:45

2 Answers 2

6
$\begingroup$

You can temporarily unset ev while creating the definition for fun[...] = ...

Block[{ev},
  fun[c_, k_, kF_, LatticeSize_] = generate[];
]

The resulting definition for fun will contain ev now:

fun[c_,k_,kF_,LatticeSize_]=2 ev[c,k,kF,LatticeSize]

Thus you will need to maintain the value of the global variable ev for this to work.

What if we want to inline the value of ev into the definition? We can do this:

Block[{CompiledFunction},
  fun[c_, k_, kF_, LatticeSize_] = generate[];
]

This will prevent the errors from CompiledFuction by disabling it during the definition process. The resulting definition will be

fun[c_,k_,kF_,LatticeSize_]=2 CompiledFunction[...][c,k,kF,LatticeSize]

Note that even if you don't disable it temporarily, the errors can be safely ignored. The definition will still be usable, if ugly.


Unfortunately, all these are really just hacks. They are convenient hacks for the problem at hand, but they do not generalize well.

A more general problem would be inlining ev without knowing its value beforehand. Here I made use of the fact that the value of ev was a CompiledFunction. I knew precisely what to disable using Block to prevent unwanted evaluation.

In order to solve this more general problem, you may need to operate on the DownValues of fun and use advanced methods such as the Trott–Strzebonski in-place evaluation technique.

I would be very interested to see a nice and general solution to this more complicated problem without resorting to such esoteric techniques. EDIT: See @MrWizard's solution.

$\endgroup$
2
  • $\begingroup$ @Mr.Wizard I wouldn't miss it because I am familiar with this undocumented form of Function. I would be looking for the first argument if I don't see it, so I would notice the comma. I added it for the benefit of those who haven't seen this syntax before. They may not notice the comma because it's so tiny ... $\endgroup$
    – Szabolcs
    Commented Feb 16, 2017 at 14:13
  • 1
    $\begingroup$ I know, I just had to tease you. :D $\endgroup$
    – Mr.Wizard
    Commented Feb 16, 2017 at 14:14
4
$\begingroup$

This kind of partial evaluation can be done using my step function from How do I evaluate only one step of an expression?

Function[Null, fun[c_, k_, kF_, LatticeSize_] := #, HoldAll] @@ step[generate[]]

?fun
fun[c_, k_, kF_, LatticeSize_] := 2 ev[c, k, kF, LatticeSize]

The advantage of this over Block is that you do not need to know in advance what ev is.

$\endgroup$
5
  • $\begingroup$ I know the Null is not necessary, but it's so easy to miss that tiny comma ... $\endgroup$
    – Szabolcs
    Commented Feb 16, 2017 at 14:11
  • 1
    $\begingroup$ I keep my promise and accept Szabolcs' answer but definitely +1 here, too. ;) $\endgroup$
    – pbx
    Commented Feb 16, 2017 at 14:16
  • $\begingroup$ I don't remember step off the top of my head, but doesn't that cause overhead in terms of evaluating generate[] more than once, since there's a call to trace and so on? $\endgroup$
    – LLlAMnYP
    Commented Feb 16, 2017 at 23:59
  • $\begingroup$ @LLlAMnYP step is only used to create the definition of fun; once that is defined it is as if you had typed in in by hand. There is no overhead of any signification while defining fun either as evaluation stops as soon as the definition of generate[] is pulled. That was one of the primary requirements for step before I figured out how to do it. You could do the same thing in this case with Hold[generate[]] /. DownValues[generate] but I find step both cleaner and more general. $\endgroup$
    – Mr.Wizard
    Commented Feb 17, 2017 at 2:41
  • $\begingroup$ I still have some doubts, but I'm really not sure how to check it rigorously. (output of TracePrint[step[f[...]]] vs. TracePrint[f[...]] bugs me a bit). There might also be the issue, that to get from generate[] to 2 ev [...] may take more than one step, but that's unanswerable without details of generate[]. $\endgroup$
    – LLlAMnYP
    Commented Feb 17, 2017 at 8:55

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.