Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions gpfa/__init__.py → blockinvgpfa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
:copyright: Copyright 2021 Brooks M. Musangu and Jan Drugowitsch.
:license: Modified BSD, see LICENSE.txt for details.
"""
from .gpfa import GPFA
from .blockinvgpfa import BlockInvGPFA
from .preprocessing import EventTimesToCounts

__all__ = [
"GPFA",
"BlockInvGPFA",
"EventTimesToCounts"
]
49 changes: 30 additions & 19 deletions gpfa/gpfa.py → blockinvgpfa/blockinvgpfa.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,18 @@
from tqdm import trange

__all__ = [
"GPFA"
"BlockInvGPFA"
]


class GPFA(sklearn.base.BaseEstimator):
"""Gaussian Process Factor Analysis (GPFA) is a probabilistic
dimensionality reduction technique that extracts smooth latent
trajectories from noisy, high-dimensional time series data.
It combines Factor Analysis (FA) for dimensionality reduction with Gaussian
Processes (GPs) to model the time courses of the latent factors in a
unified, probabilistic framework.
class BlockInvGPFA(sklearn.base.BaseEstimator):
"""BlockInvGPFA is a high-performance implementation of Gaussian
Process Factor Analysis (GPFA) is a probabilistic dimensionality
reduction technique that extracts smooth latent trajectories from
noisy, high-dimensional time series data. GPFA combines Factor
Analysis (FA) for dimensionality reduction with Gaussian Processes
(GPs) to model the time courses of the latent factors in a unified,
probabilistic framework.

GPFA operates on a set of time-series data, where each time series is
given by an ``x_dim`` x ``bins`` matrix. ``x_dim`` needs to be the same
Expand All @@ -47,6 +48,16 @@ class GPFA(sklearn.base.BaseEstimator):
Please consult the :ref:`main page <gpfa_prob_model>` for more details on
the underlying probabilistic model.

This implementation, BlockInvGPFA, builds on the core model described
in [Yu et al., 2009] and is adapted from the Elephant's toolkit, with
substantial algorithmic enhancements:

1. It uses block-matrix identities and Schur complement updates to
efficiently share kernel inversion computations across
variable-length trials.
2. It introduces a new variance-explained metric to evaluate BlockInvGPFA
model fits

Parameters
----------
bin_size : float, optional, default=0.02
Expand Down Expand Up @@ -194,45 +205,45 @@ class GPFA(sklearn.base.BaseEstimator):
Examples
--------
>>> import numpy as np
>>> from gpfa import GPFA
>>> from blockinvgpfa import BlockInvGPFA

>>> X = np.array([[
... [3, 1, 0, 4, 1, 2, 1, 3, 4, 2, 2, 1, 2, 0, 0, 2, 2, 5, 1, 3],
... [1, 0, 2, 0, 2, 1, 4, 2, 0, 1, 4, 4, 1, 2, 8, 3, 2, 1, 3, 1],
... [2, 2, 1, 1, 3, 2, 3, 2, 2, 0, 3, 4, 1, 2, 3, 1, 4, 1, 0, 1]
... ]])
>>>
>>> gpfa_model = GPFA(z_dim=1)
>>> blockinv_gpfa = BlockInvGPFA(z_dim=1)

>>> # Fit the model
>>> gpfa_model.fit(X)
>>> blockinv_gpfa.fit(X)
Initializing parameters using factor analysis...
Fitting GPFA model...
Fitting GP parameters by EM...

>>> # Infere latent variable time-courses for the same data
>>> Zs, _ = gpfa_model.predict()
>>> Zs, _ = blockinv_gpfa.predict()
>>> print(Zs)
[array([[-1.18405633, -2.00878 , -0.01470251, -2.3143544 , 0.03651376,
-1.06948736, 2.05355342, -0.16920794, -2.26437342, -1.21934552,
1.98656088, 2.13305066, -1.14113106, 0.11319858, 6.14998095,
0.89241818, 0.03509708, -1.3936327 , 0.84781358, -1.2281484 ]])]

>>> # Display the loading matrix (C_) and observation mean (d_) parameters
>>> print(gpfa_model.C_)
>>> print(blockinv_gpfa.C_)
[[-0.78376501]
[ 1.77773876]
[ 0.51134474]]

>>> print(gpfa_model.d_)
>>> print(blockinv_gpfa.d_)
[1.95470037 2.08933859 1.89693338]

>>> # Obtaining log-likelihood scores
>>> llhs = gpfa_model.score()
>>> llhs = blockinv_gpfa.score()
>>> print(llhs)
[-117.54588379661148, -107.17193271370158, ..., -100.13200154180569]

>>> # Evaluate the total explained variance regression score
>>> print(gpfa_model.variance_explained())
>>> print(blockinv_gpfa.variance_explained())
(0.6581475357501596, array([0.65814754]))


Expand Down Expand Up @@ -761,7 +772,7 @@ def _em(self, X):

if np.any(np.diag(self.R_) == var_floor):
warnings.warn('Private variance floor used for one or more '
'observed dimensions in GPFA.')
'observed dimensions in BlockInvGPFA.')

self.fit_info_ = {'iteration_time': iter_time, 'log_likelihoods': lls}

Expand Down Expand Up @@ -1188,7 +1199,7 @@ def _fill_p_auto_sum(self, Seqs, precomp):
on the caller (which should be :func:`_learn_gp_params()`) to make
sure this is called correctly.

Finally, see the notes in the GPFA README.
Finally, see the notes in the BlockInvGPFA README.
"""
############################################################
# Fill out PautoSum
Expand Down
File renamed without changes.
Binary file removed docs/_static/log-likelihoods.png
Binary file not shown.
Binary file removed docs/_static/reach_trajectories_48.gif
Binary file not shown.
Binary file removed docs/_static/reach_trajectories_48_mult_params.gif
Binary file not shown.
7 changes: 7 additions & 0 deletions docs/blockinv_gpfa_class.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
==================
BlockInvGPFA Class
==================

.. currentmodule:: blockinvgpfa
.. autoclass:: BlockInvGPFA
:members:
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import os
from datetime import date

# include root path to allow autodoc to find gpfa module
# include root path to allow autodoc to find blockgpfa module
sys.path.insert(0, os.path.abspath('../'))

# Path to the source directory (where your .rst files are)
Expand All @@ -22,7 +22,7 @@

# -- General configuration -----------------------------------------------

project = 'GPFA Documentation'
project = 'BlockInvGPFA Documentation'
authors = 'Brooks M. Musangu and Jan Drugowitsch'
copyright = "2021-{this_year}, {authors}".format(
this_year=date.today().year, authors=authors)
Expand Down
12 changes: 6 additions & 6 deletions docs/contribute.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Contribution
==============

We welcome contributions to the GPFA package, whether they involve enhancements, bug fixes,
We welcome contributions to the BlockInvGPFA package, whether they involve enhancements, bug fixes,
or documentation improvements. To ensure the codebase's quality and maintainability, please
follow this guide carefully.

Expand Down Expand Up @@ -33,9 +33,9 @@ include tests for any module, class, or function you contribute.

**Version Control Workflow**

When contributing to the GPFA package, please follow these steps:
When contributing to the BlockInvGPFA package, please follow these steps:

1. Fork the GPFA repository on GitHub.
1. Fork the BlockInvGPFA repository on GitHub.

2. Clone your forked repository to your local machine.

Expand All @@ -50,12 +50,12 @@ When contributing to the GPFA package, please follow these steps:

7. Push your branch to your GitHub fork.

8. Create a pull request (PR) from your branch to the GPFA main repository. Provide a
8. Create a pull request (PR) from your branch to the BlockInvGPFA main repository. Provide a
detailed description of your changes in the PR.

9. We will review your PR, and if necessary, provide feedback or request changes.

10. Once your PR is approved, it will be merged into the main GPFA repository.
10. Once your PR is approved, it will be merged into the main BlockInvGPFA repository.

Thank you for your contribution to the GPFA package. Your efforts help improve
Thank you for your contribution to the BlockInvGPFA package. Your efforts help improve
and maintain this valuable resource for the community.
7 changes: 0 additions & 7 deletions docs/gpfa_class.rst

This file was deleted.

36 changes: 22 additions & 14 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Gaussian-process factor analysis (GPFA) is a dimensionality reduction method [Yu
GPFA applies `factor analysis (FA) <https://scikit-learn.org/stable/modules/generated/sklearn.decomposition.FactorAnalysis.html>`_ to observed data to simultaneously reduce their dimensionality and smooth the resulting low-dimensional trajectories by fitting a `Gaussian process (GP) <https://scikit-learn.org/stable/modules/generated/sklearn.gaussian_process.kernels.Kernel.html#sklearn.gaussian_process.kernels.Kernel>`_ model to them.
See the below :ref:`notes` for its main differences to the popular PCA dimensionality redunction method.

This documentation describes `BlockInvGPFA`, a Python implementation of the GPFA model that incorporates several performance and usability improvements over existing implementations (e.g., from the Elephant toolbox [Denker2018]_).

The essential ingredients of the generative model underlying GPFA are *observations* :math:`\boldsymbol{X}` and *latent variables* :math:`\boldsymbol{Z}`.
For each high-dimensional time series in a dataset, the :math:`x_\textrm{dim}`-dimensional time series is split into :math:`T` time bins of size :math:`\delta t`, leading to the :math:`x_\textrm{dim} \times T` matrix :math:`\boldsymbol{X}` that contains these data.
The observations in each time bin are assumed to be stochastically generated by a :math:`z_\textrm{dim}`-dimensional latent variable (:math:`z_\textrm{dim} < x_\textrm{dim}`) through some noisy linear mapping.
Expand Down Expand Up @@ -49,15 +51,15 @@ Examples

.. _example_1:

(i) Applying GPFA to synthetic data
-----------------------------------
(i) Applying BlockInvGPFA to synthetic data
-------------------------------------------

This example illustrates the application of GPFA to data analysis by encompassing synthetic data generation, GPFA model fitting, and latent variable extraction. It emphasizes a step-by-step explanation of utilizing GPFA for data analysis, highlighting key functionalities of the library.
This example illustrates the application of BlockInvGPFA to data analysis by encompassing synthetic data generation, BlockInvGPFA model fitting, and latent variable extraction. It emphasizes a step-by-step explanation of utilizing BlockInvGPFA for data analysis, highlighting key functionalities of the library.

.. code-block:: python

import numpy as np
from gpfa import GPFA
from blockinvgpfa import BlockInvGPFA
from sklearn.gaussian_process import GaussianProcessRegressor
from sklearn.gaussian_process.kernels import ConstantKernel, RBF, WhiteKernel

Expand Down Expand Up @@ -95,18 +97,18 @@ This example illustrates the application of GPFA to data analysis by encompassin
x = C @ z + np.random.normal(0, sqrtR[:, np.newaxis] , (x_dim, T_per_obs))
X.append(x)

gpfa = GPFA(bin_size=bin_size, z_dim=z_dim, em_tol=1e-3, verbose=True)
gpfa.fit(X) # this will take a while
blockinv_gpfa = BlockInvGPFA(bin_size=bin_size, z_dim=z_dim, em_tol=1e-3, verbose=True)
blockinv_gpfa.fit(X) # this will take a while

results = gpfa.predict(returned_data=['Z_mu_orth', 'Z_mu'])
results = blockinv_gpfa.predict(returned_data=['Z_mu_orth', 'Z_mu'])

print(gpfa.variance_explained())
print(blockinv_gpfa.variance_explained())

.. code-block:: console

Initializing parameters using factor analysis...

Fitting GPFA model...
Fitting GP parameters by EM...
Fitting has converged after 135 EM iterations.
(0.9834339959622203, array([0.80069798, 0.18273602]))

Expand All @@ -123,7 +125,7 @@ Let us now visualize the true orthogonalized latents against the inferred orthog

# orthogonalize true latents using inferred C
true_Z = Z
true_Z_orth = [gpfa.OrthTrans_ @ Zn for Zn in true_Z]
true_Z_orth = [blockinv_gpfa.OrthTrans_ @ Zn for Zn in true_Z]

# Extract Z_orth and pZ_mu from results
Z_orth = results[0]['Z_mu_orth']
Expand Down Expand Up @@ -158,9 +160,9 @@ Let us now visualize the true orthogonalized latents against the inferred orthog

.. _examples_2:

(ii) Applying GPFA to real neural Data
--------------------------------------
You can find a more complex example that applies GPFA to neural data used by [Even2019]_ (available at [Even2022]_) in an :doc:`neural_example`. To run this example yourself, download the :download:`Jupyter notebook containing the code <neural_example.ipynb>`.
(ii) Applying BlockInvGPFA to real neural Data
----------------------------------------------
You can find a more complex example that applies BlockInvGPFA to neural data used by [Even2019]_ (available at [Even2022]_) in an :doc:`neural_example`. To run this example yourself, download the :download:`Jupyter notebook containing the code <neural_example.ipynb>`.

Original code
-------------
Expand All @@ -175,6 +177,12 @@ References
In Journal of Neurophysiology, Vol. 102, Issue 1. pp. 614-635.
<https://doi.org/10.1152/jn.90941.2008>`_

.. [Denker2018] `Denker, M., Yegenoglu, A., and Gr\"un, S.
"Collaborative HPC-enabled workflows on the HBP Collaboratory using the Elephant framework."
In Neuroinformatics 2018, 2018, p. P19.
<https://abstracts.g-node.org/conference/NI2018/abstracts#/uuid/023bec4e-0c35-4563-81ce-2c6fac282abd>`_


.. [Even2022] `Even-Chen, Nir and Sheffer, Blue and Vyas, Saurabh and Ryu, Stephen
and Shenoy, Krishna (2022)
"Structure and variability of delay activity in premotor cortex"
Expand All @@ -190,7 +198,7 @@ References
:maxdepth: 2
:hidden:

gpfa_class
blockinv_gpfa_class
preprocessing_class
neural_example
installation
Expand Down
63 changes: 38 additions & 25 deletions docs/installation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,62 +5,75 @@ Installation Guide
====================

Clone the Project Locally
--------------------------
-------------------------

Begin by cloning the project's repository locally using the `git` command within a Python virtual environment. Ensure that you have `Python virtual environment`_ installed and activated before proceeding. Open a terminal and run the following commands:
Begin by cloning the project repository locally. It is strongly recommended to do this inside a `Python virtual environment`_:

.. code-block:: bash

$ git clone https://github.com/CausalityInMotion/GPFA_for_sklearn
$ cd GPFA_for_sklearn
$ git clone https://github.com/CausalityInMotion/BlockInverseGPFA.git
$ cd BlockInverseGPFA

Install Required Packages
-------------------------
Install the Package
-------------------

Once you are in the project's working directory, install the required Python packages using `pip` and the `requirements.txt` file:
Use `pip` to install the package and its core dependencies:

.. code-block:: bash

$ pip install -r requirements.txt
$ pip install .

Your environment is now properly configured to use the GPFA package.
If you also want to run tests or build documentation, you can install the appropriate optional dependencies:

Building the Documentation
--------------------------
- For testing:

To build the documentation, you will need the following Python packages:
.. code-block:: bash

- `Sphinx`_: A documentation generator.
- `Read the Docs Sphinx Theme`_: A theme for Sphinx that provides an elegant and readable documentation format.
$ pip install .[test]

You can install these packages using `pip`:
- For documentation:

.. code-block:: bash
.. code-block:: bash

$ pip install sphinx
$ pip install sphinx-rtd-theme
$ pip install .[docs]

After installing the required packages, navigate to the "docs" directory:
Building the Documentation
--------------------------

.. code-block:: bash
To build the documentation, you will need the following packages:

$ cd docs
- Sphinx_: A documentation generator.
- `Read the Docs Sphinx Theme`_: A theme for Sphinx that provides an elegant and readable documentation format.

To generate the documentation in HTML format, run the following command:
After installing the documentation dependencies, generate the HTML documentation as follows:

.. code-block:: bash

$ cd docs
$ make html

To view the generated documentation, open the HTML index page in your web browser:
To view the generated documentation in your browser:

.. code-block:: bash

$ open _build/html/index.html
$ open _build/html/index.html # On macOS

# OR
$ xdg-open _build/html/index.html # On Linux

# OR
$ start _build/html/index.html # On Windows (in cmd)

Your documentation is now available for reference.

This guide provides step-by-step instructions for installing the GPFA package and generating its documentation.
Alternatively, you can view the latest published documentation online:

📖 https://blockinversegpfa.readthedocs.io/en/latest/

About This Guide
----------------

This guide provides step-by-step instructions for installing the BlockInvGPFA package, running its test suite, and building its documentation using Python packaging standards.

.. _Python virtual environment: https://docs.python.org/3/library/venv.html
.. _Sphinx: http://www.sphinx-doc.org
Expand Down
Loading