How does PyMC3 interpret von Mises boundaries?

If I wanted to have a von Mises distribution that is bounded with a lower bound of 350 degrees and an upper bound of 30 degrees (so encompassing 40 degrees on a 0-359 continuous scale), how would I implement that? It looks like the documentation says that von Mises go from a scale of -np.pi to np.pi, so what happens if I enter a value outside of that? Does it automatically get converted into the -np.pi to np.pi scale?

For example would the following code work correctly?

a = pm.Uniform('a', lower = np.deg2rad(-10.), upper = np.deg2rad(30.))
b = pm.VonMises('b', mu=a, kappa = 30.)

Would work for what you are trying to do?

It seems the von Mises is already bounded to support -np.pi to np.pi according to the documentation. But it’s unclear, even with that documentation, how I can further bound the von Mises such that I want the upper to be -2.96 (equivalent to -170 deg.) and the lower to be 2.62 (equivalent to 150 deg.) but I don’t want pymc3 to interpret the boundaries as the reverse such that 0 would be allowed but -3 would not be allowed. I also want to know whether I need to transform/bound any distribution that is the prior for the mu in the von Mises distribution (such that the prior only inputs values between -np.pi to np.pi) or whether it will automatically get converted within the values that von Mises supports.

The distribution is periodic in 2pi. So any ‘mu’ you specify (directly or through a prior) will get folded into the -pi to pi interval modulo 2pi. You can check this by sampling with mu = mu0, mu0+2pi, mu0+4pi etc.

If I understand your question correctly, you are not trying to alter the period. You are just trying to bound the draws and then flip the interval. A combination of pm.Bound and pm.Deterministic should do it for you. If you are just trying to alter the period and the phase for the distribution, you could still use pm.Deterministic.

Oh I see. Thanks for the help about how ‘mu’ gets folded into the interval. That solves several problems but leaves me with one last concern. If I want to use a mixture of von Mises distributions, I have to specify VonMises.dist, but the use of .dist means that I can’t use pm.Deterministic to fix the folding issue. For instance,

dists = [
    pm.VonMises.dist(mu=3, kappa=10),
    pm.VonMises.dist(mu=-3, kappa=10),
y = pm.Mixture('y', w=p, comp_dists=dists, observed=obs)

Is there a way for me to use a Deterministic function or to otherwise transform the dists so that both von Mises components are within the interval -pi to pi?

For anyone wondering how to fix the von Mises between -pi and pi (given that you are not using a mixture), the easiest way I could think of was to create a custom function:

from theano.compile.ops import as_op
@as_op(itypes=[tt.dscalar], otypes=[tt.dscalar])
def within_pi_op(k):
while (k<-np.pi or k>np.pi):
k[k>np.pi] -= np.pi2
k[k<-np.pi] += np.pi
return k

and then

vm = pm.VonMises(‘vm’,mu=3,kappa=10)
a = pm.Deterministic(‘a’, within_pi_op(vm))

The problem is I can’t use pm.Deterministic.dist so I can’t transfer this to using a mixture of von Mises.

Ok, so I think I have it working now. But it required monkey patching which I realize is bad coding practice. I’d love any ideas on how to get this implemented without monkey patching.

Here’s how PyMC3 currently deals with a mixture of von Mises distributions (using pm.Bound does not fix it and instead removes the values outside the boundaries):

And here’s what I wanted it to do and what it currently does after monkey patching:

The patch I did was to go inside the class VonMises(Continuous) and change the “def random” so that its output of generate_samples is now within_pi(generate_samples), where within_pi just takes all the values lower than -pi and all the values greater than pi and adds 2pi or subtracts 2pi respectively.