I have these inputs and functions
p = np.array([1., 0., 0.])
q = np.array([0., 0., 1.])
m = np.array([0.5, 0. , 0.5])
def kl(p, q):
return np.sum(np.log2(np.divide(p, q, where=q!=0), where=p!=0))
def js(p, q):
m = 1/2 * (p q)
return 1/2 * (kl(p, m) kl(q, m))
I get these outputs:
>>> js(p, q)
3.0
>>> kl(p, m)
2.0
>>> kl(p, m)
1.0
I was expecting:
>>> js(p, q)
1.0
>>> kl(p, m)
1.0
>>> kl(p, m)
1.0
Why are the outputs wrong? I have tried breaking it up as follows:
def kl(j, m):
s = np.divide(j, m, where=m!=0)
t = np.log2(s, where=j!=0)
return np.sum(t)
But I still get the wrong output. Only when I break up the code by adding a print() do I get the correct result:
def kl(j, m):
s = np.divide(j, m, where=m!=0)
print(s)
t = np.log2(s, where=j!=0)
return np.sum(t)
>>> js(p, q)
[2. 0. 0.]
[0. 0. 2.]
1.0
CodePudding user response:
Focusing just on the divide
. Without the where
:
In [10]: p/q
C:\Users\paul\AppData\Local\Temp\ipykernel_1128\1155702164.py:1: RuntimeWarning: divide by zero encountered in divide
p/q
C:\Users\paul\AppData\Local\Temp\ipykernel_1128\1155702164.py:1: RuntimeWarning: invalid value encountered in divide
p/q
Out[10]: array([inf, nan, 0.])
With the where
, only the last term is calculated. The rest are "random" (as in the sense of np.empty(3)
:
In [11]: np.divide(p, q, where=q!=0)
Out[11]: array([1.04756264e-311, 0.00000000e 000, 0.00000000e 000])
But we can define an out
array:
In [12]: res = np.zeros_like(p)
In [13]: np.divide(p, q, where=q!=0, out=res)
Out[13]: array([0., 0., 0.])
Or to make it a bit more obvious:
In [15]: res = np.ones_like(p)
In [16]: res
Out[16]: array([1., 1., 1.])
In [17]: np.divide(p, q, where=q!=0, out=res)
Out[17]: array([1., 1., 0.])
It's up to you to choose the correct out
for both the divide
and the log
.