Skip to content

Add Wishart with Bartlett decomposition. #701

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 15, 2016
Merged

Add Wishart with Bartlett decomposition. #701

merged 1 commit into from
Jun 15, 2016

Conversation

twiecki
Copy link
Member

@twiecki twiecki commented Apr 27, 2015

Closes #538.

This adds the Wishart distribution that can be sampled from because it's using matrix decomposition to sample from an unconstrained space.
However, it's a bit odd as it adds RVs itself to the model. I think it's pretty convenient though. @jsalvatier @fonnesbeck thoughts on adding this?

@jsalvatier
Copy link
Member

I think this looks pretty good, but I think long term it might be nicer to maybe have something like

w = Wishart(..., transform=Bartlett) 

that does this? Maybe that doesn't really work here.

Also an example of usage would be nice.

@twiecki
Copy link
Member Author

twiecki commented May 27, 2015

Right, in this case it would be pretty difficult to do that. What do you think of adding new RVs under the hood? Can't really think of a better way unfortunately. I'll add an example.

@twiecki
Copy link
Member Author

twiecki commented Jun 4, 2015

Depends on #731

@twiecki twiecki changed the title WIP Add Wishart with Bartlett decomposition. Add Wishart with Bartlett decomposition. Jun 4, 2015
@jsalvatier
Copy link
Member

Hey with the new automatic transforms I think this should be possible.

@twiecki
Copy link
Member Author

twiecki commented Jun 5, 2015

I'm not sure but I think this is not possible here. It's not really a transform of the Wishart but rather a reformulation. The Wishart is actually never used explicitly. Or do you have another idea?

@jsalvatier
Copy link
Member

Hmm, yes I see that now. We could leave it as is and I think that would be fine.

We can also create a transform that does the bartlett decomposition (and the reverse), X = L * A * A.T *L.T. That's a transform from unconstrained spaces to the positive definite space, right? So that should work as a transform for using the Wishart.

@twiecki
Copy link
Member Author

twiecki commented Jun 5, 2015

But there's no function I know of to go from X to L and A.

@jsalvatier
Copy link
Member

Ah, interesting.

Right now we require it, but I wonder if that's really necessary. I think probably the only thing we use it for is to move the default point, but we could probably not require that. The other thing that would be needed is the log determinant of the jacobian, which might be tricky, but I don't know.

@twiecki
Copy link
Member Author

twiecki commented Jun 6, 2015

Well we definitely need the direction from Wishart -> transformed Wishart, and that's the missing step.

Also, the transformation only works by adding two other RVs so I don't see a way to fit this into the current transform framework (although it would be great if it were possible).

@jsalvatier
Copy link
Member

I don't see why we need the direction Wishart -> transformed Wishart. It mostly seems like we need the direction inputs -> wishart -> rest of the model, which is transformed Wishart -> Wishart -> rest of the model. Except for getting a good starting value from the wishart distribution, but again, I think we can probably be clever about that.

@twiecki
Copy link
Member Author

twiecki commented Jun 7, 2015

I might be confused about transforms, help me clear that up:
pm.Wishart('w', prec, v, transform=pm.Bartlett)
Under the hood, pm.Bartlett gets passed prec and v. These inputs are supposed to be in some unconstrained space. The job of Bartlett then is to transform prec and v into the constrained space so that the vanilla Wishart can evaluate the logp. So you're right that we only really need the unconstrained->constrained direction.

But I'm not sure if we even have that. Bartlett starts with separate RVs that it combines that just happen to be distributed the same as a Wishart, but there is no function I know of to go from one to the other.

@twiecki
Copy link
Member Author

twiecki commented Jun 7, 2015

Maybe I'm missing something. If it's easy you should be able to provide me with that function and I'd be happy to include it, but I really don't know how to do that.

@jsalvatier
Copy link
Member

def transform(x): 
    diag_idx = np.diag_indices_from(S)
    tril_idx = np.tril_indices_from(S, k=-1)
    n_diag = len(diag_idx[0])
    n_tril = len(tril_idx[0])
    c = x[:n_diag]
    z = x[n_diag:]
    # Construct A matrix
    A = zeros(S.shape, dtype=np.float32)
    A = set_subtensor(A[diag_idx], c)
    A = set_subtensor(A[tril_idx], z)

    # L * A * A.T * L.T ~ Wishart(L*L.T, nu)

    return dot(dot(dot(L, A), A.T), L.T)

Of course there are a few problems. One, you have to make x the size of n_diag before its passed in, which we currently do by doing a backwards transform, but that's not an option here. You also clearly need to pass in L somehow, which is not currently supported. You also need to compute the jacobian determinant, which might be difficult. I haven't attempted that.

These things seem fixable. Is there something I'm missing?

@twiecki
Copy link
Member Author

twiecki commented Jun 7, 2015

Another problem is that it's not enforcing x and S to have the right distributions:

    If L ~ [[sqrt(c_1), 0, ...],
             [z_21, sqrt(c_1), 0, ...],
             [z_31, z32, sqrt(c3), ...]]
    with c_i ~ Chi²(n-i+1) and n_ij ~ N(0, 1), then
    L * A * A.T * L.T ~ Wishart(L * L.T, nu)

@twiecki
Copy link
Member Author

twiecki commented Jun 7, 2015

Or do you assume that those would be specified in the model itself?

@jsalvatier
Copy link
Member

If I understand things correctly (and I may not), we're just using the pdf of the Wishart instead of the Chi and Normal densities, so we don't need to do anything extra (except the jacobian det).

@twiecki
Copy link
Member Author

twiecki commented Jun 7, 2015

Well, the key is that we don't need to deal with the wishart which is impossible to sample from. Instead we are sampling from a chi and normal and combine them in a way so that the result is distributed like a Wishart (but the Wishart pdf never enters into the equation here). Seems like you are talking about a different transform -- what is the input x for your transform? A wishart pdf?

@jsalvatier
Copy link
Member

x is a pure theano variable with no likelihood attached.

The issue is that we can't sample from the Wishart because its too constrained. All the values we propose are illegal.

So we reformulate in a lower dimensional space (with roughly half the elements), x is in that lower dimensional space. Then we transform back into the original space to compute the density (which now needs a log jacobian determinant adjustment).

Basically the thing I'm proposing is to compute the density in the original wishart space rather than the new Chi/Normal space. It may turn out that computing the density is much more efficient in the Chi/Normal space, in which case I think your original proposal is the roughly right (I actually have an alternative, but we'll leave that for later).

@twiecki
Copy link
Member Author

twiecki commented Jun 8, 2015

Ah, I had assumed you wanted to do the Bartlett decomposition but you're proposing something else entirely. Instead, you just sample the upper triangular and then create a positive symmetric matrix from there, correct? I think we do something similar for the LKJ prior.

@jsalvatier
Copy link
Member

Yeah, exactly.

On Mon, Jun 8, 2015 at 1:07 AM, Thomas Wiecki [email protected]
wrote:

Ah, I had assumed you wanted to do the Bartlett decomposition but you're
proposing something else entirely. Instead, you just sample the upper
triangular and then create a positive symmetric matrix from there, correct?
I think we do something similar for the LKJ prior.


Reply to this email directly or view it on GitHub
#701 (comment).

@twiecki
Copy link
Member Author

twiecki commented Jun 8, 2015

That might help with the symmetry but I don't think it enforces positive definitness, no?

@jsalvatier
Copy link
Member

I think it should since in the bartlett decomposition the off diagonal
entries are independent normals. Though the diagonal elements you'll have
to constrain to be positive.
On Jun 8, 2015 14:51, "Thomas Wiecki" [email protected] wrote:

That might help with the symmetry but I don't think it enforces positive
definitness, no?


Reply to this email directly or view it on GitHub
#701 (comment).

@fonnesbeck
Copy link
Member

Is there a status update on this, or should we close it?

@twiecki
Copy link
Member Author

twiecki commented Sep 25, 2015

I do plan to get back to this. It works fine except for the travis issue.

@twiecki twiecki mentioned this pull request Nov 15, 2015
@springcoil
Copy link
Contributor

Hey @twiecki do you need some help getting this into Master? Anything I can do in my period of unemployment?

@twiecki
Copy link
Member Author

twiecki commented Jan 5, 2016

@springcoil Thanks for offering. The last issue are the failing unittests on travis I wasn't able to pin down yet (and can't reproduce locally).

@twiecki
Copy link
Member Author

twiecki commented Jan 5, 2016

Just rebased this PR off of master.

@twiecki
Copy link
Member Author

twiecki commented Jan 5, 2016

======================================================================
ERROR: pymc3.tests.test_examples.test_examples2('pymc3.examples.wishart',)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/home/travis/build/pymc-devs/pymc3/pymc3/tests/test_examples.py", line 13, in check_example
    example = __import__(example_name, fromlist='dummy')
  File "/home/travis/build/pymc-devs/pymc3/pymc3/examples/wishart.py", line 35, in <module>
    start = pm.find_MAP()
  File "/home/travis/build/pymc-devs/pymc3/pymc3/tuning/starting.py", line 81, in find_MAP
    start), fprime=grad_logp_o, disp=disp, *args, **kwargs)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/scipy/optimize/optimize.py", line 789, in fmin_bfgs
    res = _minimize_bfgs(f, x0, args, fprime, callback=callback, **opts)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/scipy/optimize/optimize.py", line 861, in _minimize_bfgs
    old_fval, old_old_fval)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/scipy/optimize/optimize.py", line 695, in _line_search_wolfe12
    **kwargs)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/scipy/optimize/linesearch.py", line 101, in line_search_wolfe1
    c1=c1, c2=c2, amax=amax, amin=amin, xtol=xtol)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/scipy/optimize/linesearch.py", line 174, in scalar_search_wolfe1
    phi1 = phi(stp)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/scipy/optimize/linesearch.py", line 87, in phi
    return f(xk + s*pk, *args)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/scipy/optimize/optimize.py", line 285, in function_wrapper
    return function(*(wrapper_args + args))
  File "/home/travis/build/pymc-devs/pymc3/pymc3/tuning/starting.py", line 73, in logp_o
    return nan_to_high(-logp(point))
  File "/home/travis/build/pymc-devs/pymc3/pymc3/blocking.py", line 119, in __call__
    return self.fa(self.fb(x))
  File "/home/travis/build/pymc-devs/pymc3/pymc3/model.py", line 404, in __call__
    return self.f(**state)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/theano/compile/function_module.py", line 871, in __call__
    storage_map=getattr(self.fn, 'storage_map', None))
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/theano/gof/link.py", line 314, in raise_with_op
    reraise(exc_type, exc_value, exc_trace)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/theano/compile/function_module.py", line 859, in __call__
    outputs = self.fn()
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/theano/gof/op.py", line 879, in rval
    r = p(n, [x[0] for x in i], o)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/theano/tensor/nlinalg.py", line 284, in perform
    z[0] = numpy.asarray(numpy.linalg.det(x), dtype=x.dtype)
  File "/home/travis/miniconda2/envs/testenv/lib/python2.7/site-packages/numpy/linalg/linalg.py", line 1776, in det
    r = _umath_linalg.det(a, signature=signature)
FloatingPointError: invalid value encountered in det
Apply node that caused the error: Det(prec)
Toposort index: 21
Inputs types: [TensorType(float64, matrix)]
Inputs shapes: [(3, 3)]
Inputs strides: [(24, 8)]
Inputs values: ['not shown']
Outputs clients: [[InplaceDimShuffle{x}(Det.0)]]
Backtrace when the node is created:
  File "/home/travis/build/pymc-devs/pymc3/pymc3/distributions/multivariate.py", line 59, in logp
    result = k * log(2*pi) + log(1./det(tau))
HINT: Use the Theano flag 'exception_verbosity=high' for a debugprint and storage map footprint of this apply node.
-------------------- >> begin captured stdout << ---------------------
Applied log-transform to c and added transformed c_log to model.
Failed to compute determinant [[ nan  nan  nan]
 [ nan  nan  nan]
 [ nan  nan  nan]]
--------------------- >> end captured stdout << ----------------------

@springcoil
Copy link
Contributor

Does this work now with the recent updates of Theano etc? I haven't had time to try it.

@twiecki
Copy link
Member Author

twiecki commented Jun 14, 2016

I haven't tried. It should.

@springcoil
Copy link
Contributor

Ok I pushed this through - after doing some rebasing - it should work.

DOC Add encoding information.

Add WishartBartlett example.

ENH Add run function to wishart example.

BUG Wishart example did not use proper namespace.

ENH Fix imports. Add verbosity arguments.

ENH: Fixing up imports

ENH: Fixing up multivariate imports
@springcoil
Copy link
Contributor

Since the tests pass I'll merge this.

@springcoil springcoil merged commit d165573 into master Jun 15, 2016
@twiecki twiecki deleted the wishart branch June 15, 2016 18:30
@twiecki
Copy link
Member Author

twiecki commented Jun 15, 2016

Awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Wishart fails with valid arguments
4 participants