4
$\begingroup$

Not really sure what the right question to ask is. But here's what I'm having trouble with:

ClearAll[Evaluate[Context[] <> "*"]]
klDivergence[d1_, d2_] := NExpectation[Log[ PDF[d1, x]/PDF[d2, x] ], x \[Distributed] d1]
d1 = NormalDistribution[0, 1];
d2 = NormalDistribution[5, 1]; 
klDivergence[d1, d2]
x=0
klDivergence[d1, d2]

The first call to klDivergence works fine. The second one gives me an error or warning "NIntegrate::itraw: Raw object 0 cannot be used as an iterator."

What's the right way to code this, so that I can have a variable of integration inside my function that is not affected by external code?

$\endgroup$

2 Answers 2

6
$\begingroup$

Localize x in a Module:

klDivergence[d1_, d2_] := Module[{x}, 
  NExpectation[Log[PDF[d1, x]/PDF[d2, x]], x \[Distributed] d1]]
$\endgroup$
4
  • $\begingroup$ A minor oversight in the language is that With[{x}, ...] is not allowed. It would be nice to be able to immutably localize a free variable, thus also protecting it from clobberage inside the function. $\endgroup$
    – John Doty
    Sep 1, 2018 at 16:30
  • 1
    $\begingroup$ One could also use \[FormalX] as in klDivergence[d1_, d2_] := NExpectation[ Log[PDF[d1, \[FormalX]]/ PDF[d2, \[FormalX]]], \[FormalX] \[Distributed] d1]. It cannot be overwritten because it is Protected. Comes close to With[{x}, ...], doesn't it? $\endgroup$ Sep 1, 2018 at 17:23
  • $\begingroup$ @HenrikSchumacher It can be overwritten with Block[{\[FormalX] = 1},...]. $\endgroup$
    – Michael E2
    Sep 1, 2018 at 18:18
  • $\begingroup$ @MichaelE2 Oha, you're right! Okay, a further reason not to use it. =D $\endgroup$ Sep 1, 2018 at 18:20
2
$\begingroup$

John Doty's Module method is used a lot. Alternatively, some internal functions use K[n], where n is an integer chosen to make K[n] unique, especially for the dummy variable of integration or summation. Something like the following effectively carries this out:

klDivergence[d1_, d2_] := 
  With[{serno = Max@{0, Cases[{d1, d2}, K[n_Integer] :> n, Infinity]} + 1},
   NExpectation[Log[PDF[d1, K[serno]]/PDF[d2, K[serno]]],
    K[serno] \[Distributed] d1]
   ];

However K is not Protected, so the usage is not foolproof. A user can assign a definition to K and mess the function up; it would also mess up internal functions that use K. So if what's good enough for Wolfram is good enough for you, you have a solution.

A little safer would be to Block K.

klDivergence[d1_, d2_] := Block[{K},
   With[{serno = Max@{0, Cases[{d1, d2}, K[n_Integer] :> n, Infinity]} + 1},
    NExpectation[Log[PDF[d1, K[serno]]/PDF[d2, K[serno]]], 
     K[serno] \[Distributed] d1]
    ]];

This would block any user definition of K while NExpectation is being computed. That's probably okay in this use-case, as well as most use cases; however, to block a user's definition from working does seem like a hole for a potential bug to creep through. Of course a user should not be assigning values to K; that explicit advice is kept from users (i.e., it is not found in the documentation, at least not easily), but it is implied by the advice, "you should always choose names for your own variables that start with lowercase letters."

Another method used sometimes is to create one's own, perhaps Private`, context; for example, foo`x or foo`Private`x:

klDivergence[d1_, d2_] := Block[{foo`x},
   NExpectation[Log[PDF[d1, foo`x]/PDF[d2, foo`x]], 
    foo`x \[Distributed] d1]
   ];
$\endgroup$

Your Answer

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

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