Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
90 commits
Select commit Hold shift + click to select a range
fb18dc2
initial commit of matrix normal base API , regression, and MNRSA
Mar 2, 2018
94dfcbb
add tensorflow to requirements
Mar 5, 2018
7264b3f
make the linter happy
Mar 12, 2018
aa54772
Merge pull request #1 from brainiak/master
narayanan2004 Mar 19, 2018
6e9fd68
Merge branch 'matnormal-regression-rsa' of https://github.com/mshvart…
Mar 19, 2018
34087a7
Fix style issues
Apr 2, 2018
4eb39f6
Merge branch 'master' into matnormal-regression-rsa
mshvartsman Apr 3, 2018
6ab49df
Merge pull request #1 from narayanan2004/matnormal-regression-rsa
mshvartsman Apr 3, 2018
347cdc8
Merge branch 'matnormal-regression-rsa' of github.com:mshvartsman/bra…
Apr 3, 2018
a75d866
more linter fixes
Apr 3, 2018
3dcf915
fix ambiguous varname
Apr 3, 2018
e9da333
linter fixes in tests
Apr 8, 2018
5ae24f1
broke this to make the linter happy, fixing
Apr 8, 2018
01aed20
more cleanup from hacky copypaste-squash
Apr 9, 2018
9ca2cef
More linter checks (for some reason run-checks.sh ignores /tests on m…
Apr 9, 2018
9ee9734
fixing sphinx complaints
Apr 9, 2018
39fbc3b
Merge remote-tracking branch 'upstream/master' into matnormal-regress…
Apr 16, 2018
e72998d
sync with upstream
mshvartsman Jul 18, 2019
56cc6c8
move kronecker solvers to their own file, utils.py was getting unwieldy
mshvartsman Jul 18, 2019
e5a2e94
initial refactor, all tests pass
mshvartsman Jan 1, 2020
bb57f74
remove CovTFWrap, use constant cholesky cov with passed Sigma instead
mshvartsman Jan 1, 2020
1ee89b3
linter and formatting fixes
mshvartsman Jan 1, 2020
835558a
merge upstream
mshvartsman Jan 2, 2020
231391b
add metaclass reference
mshvartsman Jan 8, 2020
d2bb285
further cleanup post refactor, doc changes, addressing minor comments
mshvartsman Jan 8, 2020
af2e0ac
Merge remote-tracking branch 'upstream/master' into matnormal-regress…
mshvartsman Mar 2, 2020
79092ee
linter and deprecation fixes, rename Matnorm to Matnormal in one spot
mshvartsman Mar 2, 2020
1fedd0c
fix missing comma
mshvartsman Mar 3, 2020
87eb10d
strict linter fixes
mshvartsman Mar 3, 2020
bc87a00
need old TF for things to work
mshvartsman Mar 3, 2020
05258b3
docstring formatting fix
mshvartsman Mar 3, 2020
c362426
run tf1 -> tf2 conversion script
mshvartsman Jun 30, 2020
16bf442
run v1 -> v2 conversion on tests
mshvartsman Jun 30, 2020
e4f28d6
tf 1 -> 2 on solvers
mshvartsman Jun 30, 2020
dea4d10
test_cov passes
mshvartsman Jun 30, 2020
a5b2c7e
logp tests pass
mshvartsman Jun 30, 2020
d82a1b6
cov tests pass eager
mshvartsman Jul 27, 2020
b0eb0c6
matnorm test passes in eager
mshvartsman Jul 27, 2020
0ba7509
rest of likelihoods pass in eager
mshvartsman Jul 27, 2020
1ec4425
pack/unpack for fitting using scipy
mshvartsman Aug 5, 2020
d032b1a
do cholesky covs better now
mshvartsman Aug 5, 2020
ed63f6d
test for new packing/unpacking utils
mshvartsman Aug 5, 2020
1db0080
tests now pass eager mode
mshvartsman Aug 5, 2020
b134a05
pull out repeated code as a util
mshvartsman Aug 5, 2020
7ffbe91
Follow more standard sklearn API where est params come out as trailin…
mshvartsman Aug 5, 2020
9a6b4b7
mnrsa now works with eager, tests pass
mshvartsman Aug 5, 2020
b041460
final removal of tf print and session stuff
mshvartsman Aug 5, 2020
cee8c81
typo fixes
mshvartsman Aug 6, 2020
6e95082
likelihood docstrings
mshvartsman Aug 6, 2020
12eff1f
simplify make_val_and_grad
mshvartsman Aug 6, 2020
41eda2b
fix old tf1 stuff that would break in tf2
mshvartsman Aug 6, 2020
3f6dde5
fix for maintaining the graph correctly
mshvartsman Aug 6, 2020
429e329
More stringent tests by doing bad initialization
mshvartsman Aug 6, 2020
ad6ce7d
Update example with new way of doing things
mshvartsman Aug 6, 2020
a1fa69e
final typo fixes
mshvartsman Aug 6, 2020
dca4b51
Merge branch 'mnorm-eager' into matnormal-regression-rsa
mshvartsman Aug 6, 2020
ee57283
autoformat
mshvartsman Aug 6, 2020
0cf0a77
remove nb_black dependency
mshvartsman Aug 6, 2020
d3c0a5a
minor docstring cleanup
mshvartsman Aug 14, 2020
73d2945
fix the kron covs to work correctly with the new optimizer wrapper
mshvartsman Aug 14, 2020
98607ea
autoformat
mshvartsman Aug 14, 2020
4fcf557
correctly pass optimizer args
mshvartsman Aug 14, 2020
a1fe394
Make test linter happy
mshvartsman Aug 14, 2020
39646f7
Merge branch 'master' into matnormal-regression-rsa
mshvartsman Aug 14, 2020
495c656
maybe this will make travis use a recent TF?
mshvartsman Aug 14, 2020
c9a5688
workaround to be able to use pymanopt (for theano) in the presence of…
mshvartsman Aug 15, 2020
c599f92
Merge branch 'master' into matnormal-regression-rsa (pull in tf2.3 fi…
mshvartsman Aug 18, 2020
570f4a2
doc build fixes
mshvartsman Aug 22, 2020
5f04a82
doc cleanup and removal of unused functions
mshvartsman Aug 22, 2020
8a41b9d
fix linter issues introduced by fixing docbuild issues
mshvartsman Aug 22, 2020
b77ddf3
remove hard tf2.3 requirement (tensorflow_probability deps should res…
mshvartsman Aug 22, 2020
c0af0e6
add reproducible rng fixture, improve test coverage, fixup linear dec…
mshvartsman Aug 22, 2020
6250fc8
don't print debug on tests, improve cov
mshvartsman Aug 22, 2020
3dfec38
improve coverage
mshvartsman Aug 22, 2020
549bf2a
docstring cleanups
mshvartsman Aug 22, 2020
d0da34c
notation consistency fix for the example too
mshvartsman Aug 22, 2020
0022cc7
addressing @mihaic's comments
mshvartsman Aug 25, 2020
1887a80
make cholesky flat/unflat use consistently TF ops
mshvartsman Aug 31, 2020
78a13a2
float comparison
mshvartsman Aug 31, 2020
55296e8
revert whitespace change
mshvartsman Sep 1, 2020
4e0397a
fix conda build for travis
mshvartsman Sep 1, 2020
3413d90
more conda build fixes
mshvartsman Sep 1, 2020
7d983da
whitespace
mshvartsman Sep 1, 2020
f9f595e
pull in TF2.3 from PyPI since conda only has 2.2
mshvartsman Sep 2, 2020
9e60f23
escape
mshvartsman Sep 3, 2020
5e472a1
Fix tensorflow-probability dependencies
mihaic Sep 25, 2020
6894f94
Make matnormal optional
mihaic Oct 9, 2020
845ca84
Add tensorflow dependency, docs
mihaic Oct 9, 2020
0da0201
Change dependencies installation instructions
mihaic Oct 13, 2020
c55e963
Merge branch 'master' into matnormal-regression-rsa
mihaic Oct 13, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .conda/bin/build
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
python_version=$1
if [ -z "$python_version" ]
then
python_version=3.6
python_version=3.7
fi

export BRAINIAK_HOME=$DIR/../../
Expand Down
4 changes: 3 additions & 1 deletion .conda/build.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/bin/bash

# Install pymanopt via pip because there isn't a conda package
# Install from PyPI because there is no current conda package for the
# following. Explicitly install dependencies with no conda package as well
# because otherwise conda-build does not include them in the output package.
PIP_NO_INDEX=False $PYTHON -m pip install pymanopt

# NOTE: This is the recommended way to install packages
Expand Down
5 changes: 1 addition & 4 deletions .conda/meta.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,6 @@ requirements:
if req not in conda_package_nonexistent -%}
- {{req}}
{% endfor %}
{% for req in data.get('install_requires', [])
if req not in conda_package_nonexistent -%}
- {{req}}
{% endfor %}

run:
- python
Expand All @@ -59,6 +55,7 @@ requirements:
test:
commands:
- find $BRAINIAK_HOME/tests | grep pycache | xargs rm -rf
- pip install tensorflow tensorflow-probability
- mpiexec -n 2 pytest $BRAINIAK_HOME

# Known issue: https://github.com/travis-ci/travis-ci/issues/4704#issuecomment-348435959
Expand Down
9 changes: 8 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,14 @@ If you have `Conda <conda.io>`_::
Otherwise, or if you want to compile from source, install the requirements (see
`docs/installation`) and then install from PyPI::

python3 -m pip install --no-use-pep517 brainiak
python3 -m pip install brainiak

Note that to use the ``brainiak.matnormal`` package, you need to install
additional dependencies. As of October 2020, the required versions are not
available as Conda packages, so you should install from PyPI, even when using
Conda::

python3 -m pip install -U tensorflow tensorflow-probability

Note that we do not support Windows.

Expand Down
6 changes: 6 additions & 0 deletions brainiak/funcalign/sssrm.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
from pymanopt.solvers import ConjugateGradient
from pymanopt import Problem
from pymanopt.manifolds import Stiefel
import pymanopt

import gc

from brainiak.utils import utils
Expand All @@ -57,6 +59,10 @@
# https://github.com/pymc-devs/pymc3/pull/3767
theano.config.gcc.cxxflags = "-Wno-c++11-narrowing"

# FIXME workaround for pymanopt only working with tensorflow 1.
# We don't use pymanopt+TF so we just let pymanopt pretend TF doesn't exist.
pymanopt.tools.autodiff._tensorflow.tf = None


class SSSRM(BaseEstimator, ClassifierMixin, TransformerMixin):
"""Semi-Supervised Shared Response Model (SS-SRM)
Expand Down
252 changes: 252 additions & 0 deletions brainiak/matnormal/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
"""
Some properties of the matrix-variate normal distribution
---------------------------------------------------------

.. math::
\\DeclareMathOperator{\\Tr}{Tr}
\\newcommand{\\trp}{^{T}} % transpose
\\newcommand{\\trace}{\\text{Trace}} % trace
\\newcommand{\\inv}{^{-1}}
\\newcommand{\\mb}{\\mathbf{b}}
\\newcommand{\\M}{\\mathbf{M}}
\\newcommand{\\C}{\\mathbf{C}}
\\newcommand{\\G}{\\mathbf{G}}
\\newcommand{\\A}{\\mathbf{A}}
\\newcommand{\\R}{\\mathbf{R}}
\\renewcommand{\\S}{\\mathbf{S}}
\\newcommand{\\B}{\\mathbf{B}}
\\newcommand{\\Q}{\\mathbf{Q}}
\\newcommand{\\mH}{\\mathbf{H}}
\\newcommand{\\U}{\\mathbf{U}}
\\newcommand{\\mL}{\\mathbf{L}}
\\newcommand{\\diag}{\\mathrm{diag}}
\\newcommand{\\etr}{\\mathrm{etr}}
\\renewcommand{\\H}{\\mathbf{H}}
\\newcommand{\\vecop}{\\mathrm{vec}}
\\newcommand{\\I}{\\mathbf{I}}
\\newcommand{\\X}{\\mathbf{X}}
\\newcommand{\\Y}{\\mathbf{Y}}
\\newcommand{\\Z}{\\mathbf{Z}}
\\renewcommand{\\L}{\\mathbf{L}}


The matrix-variate normal distribution is a generalization to matrices of the
normal distribution. Another name for it is the multivariate normal
distribution with kronecker separable covariance.
The distributional intuition is as follows: if
:math:`X \\sim \\mathcal{MN}(M,R,C)` then
:math:`\\mathrm{vec}(X)\\sim\\mathcal{N}(\\mathrm{vec}(M), C \\otimes R)`,
where :math:`\\mathrm{vec}(\\cdot)` is the vectorization operator and
:math:`\\otimes` is the Kronecker product. If we think of X as a matrix of TRs
by voxels in the fMRI setting, then this model assumes that each voxel has the
same TR-by-TR covariance structure (represented by the matrix R),
and each volume has the same spatial covariance (represented by the matrix C).
This assumption allows us to model both covariances separately.
We can assume that the spatial covariance itself is kronecker-structured,
which implies that the spatial covariance of voxels is the same in the X,
Y and Z dimensions.

The log-likelihood for the matrix-normal density is:

.. math::
\\log p(X\\mid \\M,\\R, \\C) = -2\\log mn - m \\log|\\C| - n \\log|\\R| -
\\Tr\\left[\\C\\inv(\\X-\\M)\\trp\\R\\inv(\\X-\\M)\\right]

Here :math:`X` and :math:`M` are both :math:`m\\times n` matrices, :math:`\\R`
is :math:`m\\times m` and :math:`\\C` is :math:`n\\times n`.

The `brainiak.matnormal` package provides structure to infer models that
can be stated in the matrix-normal notation that are useful for fMRI analysis.
It provides a few interfaces. `MatnormModelBase` is intended as a
base class for matrix-variate models. It provides a wrapper for the tensorflow
optimizer that provides convergence checks based on thresholds on the function
value and gradient, and simple verbose outputs. It also provides an interface
for noise covariances (`CovBase`). Any class that follows the interface
can be used as a noise covariance in any of the matrix normal models. The
package includes a variety of noise covariances to work with.

Matrix normal marginals
-------------------------

Here we extend the multivariate gaussian marginalization identity to matrix
normals. This is used in a number of the models in the package. Below, we
use lowercase subscripts for sizes to make dimensionalities easier to track.
Uppercase subscripts for covariances help keep track where they come from.

.. math::
\\mathbf{X}_{ij} &\\sim \\mathcal{MN}(\\mathbf{A}_{ij},
\\Sigma_{\\mathbf{X}i},\\Sigma_{\\mathbf{X}j})\\\\
\\mathbf{Y}_{jk} &\\sim \\mathcal{MN}(\\mathbf{B}_{jk},
\\Sigma_{\\mathbf{Y}j},\\Sigma_{\\mathbf{Y}k})\\\\
\\mathbf{Z}_{ik}\\mid\\mathbf{X}_{ij},\\mathbf{Y}_{jk} &\\sim
\\mathcal{MN}(\\mathbf{X}_{ij}\\mathbf{Y}_{jk} + \\mathbf{C}_{ik},
\\Sigma_{\\mathbf{Z}_i}, \\Sigma_{\\mathbf{Z}_k})\\\\


We vectorize, and convert to a form we recognize as
:math:`y \\sim \\mathcal{N}(Mx+b, \\Sigma)`.

.. math::
\\vecop(\\mathbf{Z}_{ik})\\mid\\mathbf{X}_{ij},\\mathbf{Y}_{jk} &\\sim
\\mathcal{N}(\\vecop(\\X_{ij}\\mathbf{Y}_{jk}+\\mathbf{C}_{ik}),
\\Sigma_{\\mathbf{Z}_k}\\otimes\\Sigma_{\\mathbf{Z}_i})\\\\
\\vecop(\\mathbf{Z}_{ik})\\mid\\mathbf{X}_{ij},\\mathbf{Y}_{jk}
&\\sim \\mathcal{N}((\\I_k\\otimes\\X_{ij})\\vecop(\\mathbf{Y}_{jk})
+ \\vecop(\\mathbf{C}_{ik}),
\\Sigma_{\\mathbf{Z}_k}\\otimes\\Sigma_{\\mathbf{Z}_i})


Now we can use our standard gaussian marginalization identity:

.. math::
\\vecop(\\mathbf{Z}_{ik})\\mid\\mathbf{X}_{ij} \\sim
\\mathcal{N}((\\I_k\\otimes\\X_{ij})\\vecop(\\mathbf{B}_{jk}) +
\\vecop(\\mathbf{C}_{ik}),
\\Sigma_{\\mathbf{Z}_k}\\otimes\\Sigma_{\\mathbf{Z}_i} +
(\\I_k\\otimes\\X_{ij})(\\Sigma_{\\mathbf{Y}_k}\\otimes
\\Sigma_{\\mathbf{Y}_j})(\\I_k\\otimes\\X_{ij})\\trp )


Collect terms using the mixed-product property of kronecker products:

.. math::
\\vecop(\\mathbf{Z}_{ik})\\mid\\mathbf{X}_{ij} \\sim
\\mathcal{N}(\\vecop(\\X_{ij}\\mathbf{B}_{jk}) +
\\vecop(\\mathbf{C}_{ik}), \\Sigma_{\\mathbf{Z}_k}\\otimes
\\Sigma_{\\mathbf{Z}_i} + \\Sigma_{\\mathbf{Y}_k}\\otimes
\\X_{ij}\\Sigma_{\\mathbf{Y}_j}\\X_{ij}\\trp)


Now, we can see that the marginal density is a matrix-variate normal only if
:math:`\\Sigma_{\\mathbf{Z}_k}= \\Sigma_{\\mathbf{Y}_k}` -- that is, the
variable we're marginalizing over has the same covariance in the dimension
we're *not* marginalizing over as the marginal density. Otherwise the densit
Comment thread
mshvartsman marked this conversation as resolved.
is well-defined but the covariance retains its kronecker structure. So we let
:math:`\\Sigma_k:=\\Sigma_{\\mathbf{Z}_k}= \\Sigma_{\\mathbf{Y}_k}`, factor,
and transform it back into a matrix normal:

.. math::
\\vecop(\\mathbf{Z}_{ik})\\mid\\mathbf{X}_{ij} &\\sim
\\mathcal{N}(\\vecop(\\X\\mathbf{B}_{jk}) + \\vecop(\\mathbf{C}_{ik}),
\\Sigma_{k}\\otimes\\Sigma_{\\mathbf{Z}_i} + \\Sigma_{_k}\\otimes
\\X\\Sigma_{\\mathbf{Y}_j}\\X\\trp)\\\\
\\vecop(\\mathbf{Z}_{ik})\\mid\\mathbf{X}_{ij} &\\sim
\\mathcal{N}(\\vecop(\\X\\mathbf{B}_{jk}) + \\vecop(\\mathbf{C}_{ik}),
\\Sigma_{k}\\otimes(\\Sigma_{\\mathbf{Z}_i}
+\\X\\Sigma_{\\mathbf{Y}_j}\\X\\trp))\\\\
\\mathbf{Z}_{ik}\\mid\\mathbf{X}_{ij} &\\sim
\\mathcal{MN}(\\X\\mathbf{B}_{jk} + \\mathbf{C}_{ik},
\\Sigma_{\\mathbf{Z}_i} +\\X\\Sigma_{\\mathbf{Y}_j}\\X\\trp,\\Sigma_{k})


We can do it in the other direction as well, because if
:math:`\\X \\sim \\mathcal{MN}(M, U, V)` then :math:`\\X\\trp \\sim
\\mathcal{MN}(M\\trp, V, U)`:

.. math::
\\mathbf{Z\\trp}_{ik}\\mid\\mathbf{X}_{ij},\\mathbf{Y}_{jk} &\\sim
\\mathcal{MN}(\\mathbf{Y}_{jk}\\trp\\mathbf{X}_{ij}\\trp +
\\mathbf{C}\\trp_{ik}, \\Sigma_{\\mathbf{Z}_k},\\Sigma_{\\mathbf{Z}_i})\\\\
\\mbox{let } \\Sigma_i :=
\\Sigma_{\\mathbf{Z}_i}=\\Sigma_{\\mathbf{X}_i} \\\\
\\cdots\\\\
\\mathbf{Z\\trp}_{ik}\\mid\\mathbf{Y}_{jk} &\\sim
\\mathcal{MN}(\\mathbf{A}_{jk}\\trp\\mathbf{X}_{ij}\\trp +
\\mathbf{C}\\trp_{ik}, \\Sigma_{\\mathbf{Z}_k} +
\\Y\\trp\\Sigma_{\\mathbf{Y}_j}\\Y,\\Sigma_{\\mathbf{Z}_i})\\\\
\\mathbf{Z}_{ik}\\mid\\mathbf{Y}_{jk} &\\sim
\\mathcal{MN}(\\mathbf{X}_{ij}\\mathbf{A}_{jk}+
\\mathbf{C}_{ik},\\Sigma_{\\mathbf{Z}_i},\\Sigma_{\\mathbf{Z}_k} +
\\Y\\trp\\Sigma_{\\mathbf{Y}_j}\\Y)

These marginal likelihoods are implemented relatively efficiently in
`MatnormModelBase.matnorm_logp_marginal_row` and
`MatnormModelBase.matnorm_logp_marginal_col`.

Partitioned matrix normal conditionals
--------------------------------------

Here we extend the multivariate gaussian conditional identity to matrix
normals. This is used for prediction in some models. Below, we
use lowercase subscripts for sizes to make dimensionalities easier to track.
Uppercase subscripts for covariances help keep track where they come from.


Next, we do the same for the partitioned gaussian identity. First two
vectorized matrix-normals that form our partition:

.. math::
\\mathbf{X}_{ij} &\\sim \\mathcal{MN}(\\mathbf{A}_{ij}, \\Sigma_{i},
\\Sigma_{j}) \\rightarrow \\vecop[\\mathbf{X}_{ij}] \\sim
\\mathcal{N}(\\vecop[\\mathbf{A}_{ij}], \\Sigma_{j}\\otimes\\Sigma_{i})\\\\
\\mathbf{Y}_{ik} &\\sim \\mathcal{MN}(\\mathbf{B}_{ik}, \\Sigma_{i},
\\Sigma_{k}) \\rightarrow \\vecop[\\mathbf{Y}_{ik}] \\sim
\\mathcal{N}(\\vecop[\\mathbf{B}_{ik}], \\Sigma_{k}\\otimes\\Sigma_{i})\\\\
\\begin{bmatrix}\\vecop[\\mathbf{X}_{ij}] \\\\ \\vecop[\\mathbf{Y}_{ik}]
\\end{bmatrix}
& \\sim \\mathcal{N}\\left(\\vecop\\begin{bmatrix}\\mathbf{A}_{ij}
\\\\ \\mathbf{B}_{ik}
\\end{bmatrix}
, \\begin{bmatrix} \\Sigma_{j}\\otimes \\Sigma_i &
\\Sigma_{jk} \\otimes \\Sigma_i \\\\
\\Sigma_{kj}\\otimes \\Sigma_i & \\Sigma_{k} \\otimes
\\Sigma_i\\end{bmatrix}\\right)

We apply the standard partitioned Gaussian identity and simplify using the
properties of the :math:`\\vecop` operator and the mixed product property
of kronecker products:

.. math::
\\vecop[\\X_{ij}] \\mid \\vecop[\\Y_{ik}]\\sim
\\mathcal{N}(&\\vecop[\\A_{ij}] + (\\Sigma_{jk}\\otimes\\Sigma_i)
(\\Sigma_k\\inv\\otimes\\Sigma_i\\inv)(\\vecop[\\Y_{ik}]-\\vecop[\\B_{ik}]),\\\\
& \\Sigma_j\\otimes\\Sigma_i - (\\Sigma_{jk}\\otimes\\Sigma_i)
(\\Sigma_k\\inv\\otimes\\Sigma_i\\inv) (\\Sigma_{kj}\\otimes\\Sigma_i))\\\\
=\\mathcal{N}(&\\vecop[\\A_{ij}] +
(\\Sigma_{jk}\\Sigma_k\\inv\\otimes\\Sigma_i\\Sigma_i\\inv)
(\\vecop[\\Y_{ik}]-\\vecop[\\B_{ik}]), \\\\
& \\Sigma_j\\otimes\\Sigma_i -
(\\Sigma_{jk}\\Sigma_k\\inv\\Sigma_{kj}\\otimes
\\Sigma_i\\Sigma_i\\inv \\Sigma_i))\\\\
=\\mathcal{N}(&\\vecop[\\A_{ij}] + (\\Sigma_{jk}\\Sigma_k\\inv\\otimes\\I)
(\\vecop[\\Y_{ik}]-\\vecop[\\B_{ik}]), \\\\
& \\Sigma_j\\otimes\\Sigma_i -
(\\Sigma_{jk}\\Sigma_k\\inv\\Sigma_{kj}\\otimes\\Sigma_i)\\\\
=\\mathcal{N}(&\\vecop[\\A_{ij}] +
\\vecop[\\Y_{ik}-\\B_{ik}\\Sigma_k\\inv\\Sigma_{kj}],
(\\Sigma_j-\\Sigma_{jk}\\Sigma_k\\inv\\Sigma_{kj})\\otimes\\Sigma_i)


Next, we recognize that this multivariate gaussian is equivalent to the
following matrix variate gaussian:

.. math::
\\X_{ij} \\mid \\Y_{ik}\\sim \\mathcal{MN}(\\A_{ij} +
(\\Y_{ik}-\\B_{ik})\\Sigma_k\\inv\\Sigma_{kj}, \\Sigma_i,
\\Sigma_j-\\Sigma_{jk}\\Sigma_k\\inv\\Sigma_{kj})

The conditional in the other direction can be written by working through the
same algebra:

.. math::
\\Y_{ik} \\mid \\X_{ij}\\sim \\mathcal{MN}(\\B_{ik} +(\\X_{ij}-
\\A_{ij})\\Sigma_j\\inv\\Sigma_{jk}, \\Sigma_i,
\\Sigma_k-\\Sigma_{kj}\\Sigma_j\\inv\\Sigma_{jk})

Finally, vertical rather than horizontal concatenation (yielding a partitioned
row rather than column covariance) can be written by recognizing the behavior
of the matrix normal under transposition:

.. math::
\\X\\trp_{ji} \\mid \\Y\\trp_{ki}\\sim \\mathcal{MN}(&\\A\\trp_{ji} +
\\Sigma_{jk}\\Sigma_k\\inv(\\Y\\trp_{ki}-\\B\\trp_{ki}),
\\Sigma_j-\\Sigma_{jk}\\Sigma_k\\inv\\Sigma_{kj}, \\Sigma_i)\\\\
\\Y\\trp_{ki} \\mid \\X\\trp_{ji}\\sim \\mathcal{MN}(&\\B\\trp_{ki} +
\\Sigma_{kj}\\Sigma_j\\inv(\\X\\trp_{ji}-\\A\\trp_{ji}),
\\Sigma_k-\\Sigma_{kj}\\Sigma_j\\inv\\Sigma_{jk}, \\Sigma_i)

These conditional likelihoods are implemented relatively efficiently
in `MatnormModelBase.matnorm_logp_conditional_row` and
`MatnormModelBase.matnorm_logp_conditional_col`.

"""
Loading