neuralign 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. neuralign-0.1.0/.github/scripts/check_pypi_release.py +68 -0
  2. neuralign-0.1.0/.github/workflows/ci.yml +80 -0
  3. neuralign-0.1.0/.github/workflows/pages.yml +47 -0
  4. neuralign-0.1.0/.github/workflows/publish.yml +89 -0
  5. neuralign-0.1.0/CITATION.cff +36 -0
  6. neuralign-0.1.0/LICENSE +21 -0
  7. neuralign-0.1.0/PKG-INFO +74 -0
  8. neuralign-0.1.0/README.md +45 -0
  9. neuralign-0.1.0/docs/api.html +133 -0
  10. neuralign-0.1.0/docs/api.md +74 -0
  11. neuralign-0.1.0/docs/assets/neuralign-mark.png +0 -0
  12. neuralign-0.1.0/docs/assets/neuralign-mark.svg +217 -0
  13. neuralign-0.1.0/docs/assets/neuralign-overview.pdf +0 -0
  14. neuralign-0.1.0/docs/assets/neuralign-overview.png +0 -0
  15. neuralign-0.1.0/docs/assets/quickstart.png +0 -0
  16. neuralign-0.1.0/docs/assets/quickstart.py +45 -0
  17. neuralign-0.1.0/docs/build.py +51 -0
  18. neuralign-0.1.0/docs/getting-started.html +24 -0
  19. neuralign-0.1.0/docs/getting-started.md +31 -0
  20. neuralign-0.1.0/docs/index.html +31 -0
  21. neuralign-0.1.0/docs/index.md +45 -0
  22. neuralign-0.1.0/docs/model-and-outputs.html +91 -0
  23. neuralign-0.1.0/docs/model-and-outputs.md +44 -0
  24. neuralign-0.1.0/docs/reproducibility.html +15 -0
  25. neuralign-0.1.0/docs/reproducibility.md +17 -0
  26. neuralign-0.1.0/docs/style.css +24 -0
  27. neuralign-0.1.0/examples/quickstart.png +0 -0
  28. neuralign-0.1.0/examples/quickstart.py +45 -0
  29. neuralign-0.1.0/pyproject.toml +57 -0
  30. neuralign-0.1.0/src/neuralign/__init__.py +10 -0
  31. neuralign-0.1.0/src/neuralign/adapt.py +439 -0
  32. neuralign-0.1.0/src/neuralign/callbacks.py +90 -0
  33. neuralign-0.1.0/src/neuralign/configs.py +52 -0
  34. neuralign-0.1.0/src/neuralign/gauges.py +67 -0
  35. neuralign-0.1.0/src/neuralign/initializers.py +106 -0
  36. neuralign-0.1.0/src/neuralign/losses.py +402 -0
  37. neuralign-0.1.0/src/neuralign/model.py +418 -0
  38. neuralign-0.1.0/src/neuralign/neuralign_factors.py +345 -0
  39. neuralign-0.1.0/src/neuralign/ops.py +54 -0
  40. neuralign-0.1.0/src/neuralign/simulation.py +53 -0
  41. neuralign-0.1.0/src/neuralign/trainer.py +81 -0
@@ -0,0 +1,68 @@
1
+ """Decide whether a verified version is new, without hiding partial uploads."""
2
+
3
+ import json
4
+ import os
5
+ from pathlib import Path
6
+ import tomllib
7
+ from urllib.error import HTTPError
8
+ from urllib.parse import quote
9
+ from urllib.request import Request, urlopen
10
+
11
+
12
+ def needs_publishing(name, version, filenames):
13
+ """Return False only for an existing, complete, non-yanked release.
14
+
15
+ PyPI files cannot be replaced. Ordinary main updates keep their version and
16
+ skip publishing; incomplete releases require an explicit maintainer decision.
17
+ Network failures and unexpected API responses must fail the workflow.
18
+ """
19
+ url = f"https://pypi.org/pypi/{quote(name, safe='')}/{quote(version, safe='')}/json"
20
+ request = Request(
21
+ url,
22
+ headers={"Accept": "application/json", "User-Agent": "neuralign-release-check"},
23
+ )
24
+ try:
25
+ with urlopen(request, timeout=30) as response:
26
+ release = json.load(response)
27
+ except HTTPError as error:
28
+ if error.code == 404:
29
+ return True
30
+ raise
31
+
32
+ available = {
33
+ item["filename"] for item in release["urls"] if not item.get("yanked", False)
34
+ }
35
+ missing = set(filenames) - available
36
+ if missing:
37
+ raise RuntimeError(
38
+ f"PyPI already contains {name} {version}, but its release is incomplete "
39
+ f"or yanked (missing: {', '.join(sorted(missing))}). Recover the release "
40
+ "deliberately or bump the version; automatic publishing will not skip "
41
+ "individual files."
42
+ )
43
+ return False
44
+
45
+
46
+ def main():
47
+ project = tomllib.loads(Path("pyproject.toml").read_text())["project"]
48
+ name, version = project["name"], project["version"]
49
+ wheels = list(Path("dist").glob("*.whl"))
50
+ sources = list(Path("dist").glob("*.tar.gz"))
51
+ if len(wheels) != 1 or len(sources) != 1:
52
+ raise RuntimeError("Expected exactly one verified wheel and source distribution")
53
+
54
+ publish = needs_publishing(name, version, [path.name for path in wheels + sources])
55
+ outputs = f"version={version}\npublish={str(publish).lower()}\n"
56
+ if output_file := os.environ.get("GITHUB_OUTPUT"):
57
+ with open(output_file, "a", encoding="utf-8") as stream:
58
+ stream.write(outputs)
59
+ status = "will be published" if publish else "is already published; skipping upload"
60
+ message = f"{name} {version} {status}."
61
+ print(message)
62
+ if summary_file := os.environ.get("GITHUB_STEP_SUMMARY"):
63
+ with open(summary_file, "a", encoding="utf-8") as stream:
64
+ stream.write(message + "\n")
65
+
66
+
67
+ if __name__ == "__main__":
68
+ main()
@@ -0,0 +1,80 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, dev]
6
+ pull_request:
7
+ branches: [main, dev]
8
+ workflow_dispatch:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ concurrency:
14
+ group: ci-${{ github.workflow }}-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ package:
19
+ name: Python ${{ matrix.python }} / ${{ matrix.dependencies }}
20
+ runs-on: ubuntu-latest
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ include:
25
+ - python: "3.10"
26
+ dependencies: minimum
27
+ torch: torch==2.9.1
28
+ numpy: numpy==1.26.0
29
+ - python: "3.11"
30
+ dependencies: latest
31
+ torch: torch>=2.9.1
32
+ numpy: numpy>=1.26
33
+ - python: "3.12"
34
+ dependencies: latest
35
+ torch: torch>=2.9.1
36
+ numpy: numpy>=1.26
37
+ - python: "3.13"
38
+ dependencies: latest
39
+ torch: torch>=2.9.1
40
+ numpy: numpy>=1.26
41
+ - python: "3.14"
42
+ dependencies: latest
43
+ torch: torch>=2.9.1
44
+ numpy: numpy>=1.26
45
+ steps:
46
+ - uses: actions/checkout@v7
47
+ - uses: actions/setup-python@v7
48
+ with:
49
+ python-version: ${{ matrix.python }}
50
+ cache: pip
51
+ cache-dependency-path: pyproject.toml
52
+ - name: Build and validate distributions
53
+ run: |
54
+ python -m pip install build twine
55
+ python -m build
56
+ python -m twine check --strict dist/*
57
+ - name: Install the wheel with CPU dependencies
58
+ env:
59
+ TORCH_REQUIREMENT: ${{ matrix.torch }}
60
+ NUMPY_REQUIREMENT: ${{ matrix.numpy }}
61
+ run: |
62
+ python -m pip install --index-url https://download.pytorch.org/whl/cpu "$TORCH_REQUIREMENT"
63
+ python -m pip install "$NUMPY_REQUIREMENT" dist/*.whl
64
+ python -m pip check
65
+ - name: Test the installed package
66
+ run: python -I -m unittest discover -s tests -v
67
+
68
+ docs:
69
+ name: Documentation
70
+ runs-on: ubuntu-latest
71
+ steps:
72
+ - uses: actions/checkout@v7
73
+ - uses: actions/setup-python@v7
74
+ with:
75
+ python-version: "3.12"
76
+ - name: Build documentation and check generated files
77
+ run: |
78
+ python -m pip install "markdown-it-py>=3.0"
79
+ python docs/build.py
80
+ git diff --exit-code -- docs
@@ -0,0 +1,47 @@
1
+ name: Deploy docs to GitHub Pages
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+ pages: write
11
+ id-token: write
12
+
13
+ concurrency:
14
+ group: pages
15
+ cancel-in-progress: false
16
+
17
+ jobs:
18
+ deploy:
19
+ environment:
20
+ name: github-pages
21
+ url: ${{ steps.deployment.outputs.page_url }}
22
+ runs-on: ubuntu-latest
23
+ steps:
24
+ - name: Checkout
25
+ uses: actions/checkout@v4
26
+
27
+ - name: Set up Python
28
+ uses: actions/setup-python@v5
29
+ with:
30
+ python-version: "3.12"
31
+
32
+ - name: Build documentation
33
+ run: |
34
+ python -m pip install "markdown-it-py>=3.0"
35
+ python docs/build.py
36
+
37
+ - name: Setup Pages
38
+ uses: actions/configure-pages@v5
39
+
40
+ - name: Upload artifact
41
+ uses: actions/upload-pages-artifact@v3
42
+ with:
43
+ path: docs
44
+
45
+ - name: Deploy to GitHub Pages
46
+ id: deployment
47
+ uses: actions/deploy-pages@v4
@@ -0,0 +1,89 @@
1
+ name: Publish package to PyPI
2
+
3
+ on:
4
+ workflow_dispatch:
5
+
6
+ permissions:
7
+ contents: read
8
+
9
+ # A rerun or a second main update must not race an upload already in progress.
10
+ concurrency:
11
+ group: pypi-publish
12
+ cancel-in-progress: false
13
+
14
+ jobs:
15
+ build:
16
+ name: Build and verify distributions
17
+ if: github.ref == 'refs/heads/main'
18
+ runs-on: ubuntu-latest
19
+ timeout-minutes: 20
20
+ outputs:
21
+ publish: ${{ steps.release.outputs.publish }}
22
+ version: ${{ steps.release.outputs.version }}
23
+ steps:
24
+ - name: Checkout
25
+ uses: actions/checkout@v7
26
+ with:
27
+ persist-credentials: false
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v7
31
+ with:
32
+ python-version: "3.12"
33
+
34
+ - name: Build wheel and source distribution
35
+ run: |
36
+ python -m pip install --upgrade build twine
37
+ python -m build
38
+ python -m twine check --strict dist/*
39
+
40
+ - name: Install CPU PyTorch
41
+ run: python -m pip install --index-url https://download.pytorch.org/whl/cpu "torch>=2.9.1"
42
+
43
+ - name: Test installed wheel
44
+ run: |
45
+ python -m pip install dist/*.whl
46
+ python -m pip check
47
+ python -I -m unittest discover -s tests -v
48
+
49
+ - name: Test installed source distribution
50
+ run: |
51
+ python -m pip uninstall --yes neuralign
52
+ python -m pip install dist/*.tar.gz
53
+ python -m pip check
54
+ python -I -m unittest discover -s tests -v
55
+
56
+ - name: Check whether this version needs publishing
57
+ id: release
58
+ run: python .github/scripts/check_pypi_release.py
59
+
60
+ - name: Upload verified distributions
61
+ uses: actions/upload-artifact@v7
62
+ with:
63
+ name: python-distributions
64
+ path: dist/*
65
+ if-no-files-found: error
66
+ retention-days: 14
67
+
68
+ publish:
69
+ name: Publish new version
70
+ needs: build
71
+ if: github.ref == 'refs/heads/main' && needs.build.outputs.publish == 'true'
72
+ runs-on: ubuntu-latest
73
+ timeout-minutes: 10
74
+ environment:
75
+ name: pypi
76
+ url: https://pypi.org/project/neuralign/${{ needs.build.outputs.version }}/
77
+ permissions:
78
+ id-token: write
79
+ steps:
80
+ - name: Download verified distributions
81
+ uses: actions/download-artifact@v8
82
+ with:
83
+ name: python-distributions
84
+ path: dist/
85
+
86
+ # No checkout or package execution in the job with publishing permission.
87
+ # Configure this workflow and the pypi environment as a Trusted Publisher.
88
+ - name: Publish distributions with Trusted Publishing
89
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,36 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use Neuralign, please cite this software and the associated manuscript."
3
+ title: "Neuralign"
4
+ type: software
5
+ license: MIT
6
+ version: 0.1.0
7
+ repository-code: "https://github.com/MiklosKralik/neuralign"
8
+ authors:
9
+ - family-names: Králik
10
+ given-names: Miklós
11
+ - family-names: Tinelli
12
+ given-names: Sasha
13
+ - family-names: Kozloski
14
+ given-names: James
15
+ - family-names: Ponte Costa
16
+ given-names: Rui
17
+ - family-names: Sharott
18
+ given-names: Andrew
19
+ - family-names: Peach
20
+ given-names: Robert L.
21
+ preferred-citation:
22
+ type: unpublished
23
+ title: "Identifying aligned tensor decompositions across heterogeneous neural datasets"
24
+ authors:
25
+ - family-names: Králik
26
+ given-names: Miklós
27
+ - family-names: Tinelli
28
+ given-names: Sasha
29
+ - family-names: Kozloski
30
+ given-names: James
31
+ - family-names: Ponte Costa
32
+ given-names: Rui
33
+ - family-names: Sharott
34
+ given-names: Andrew
35
+ - family-names: Peach
36
+ given-names: Robert L.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Neuralign contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: neuralign
3
+ Version: 0.1.0
4
+ Summary: Joint tensor decomposition and alignment of trial-resolved neural recordings
5
+ Keywords: computational neuroscience,tensor decomposition,neural population dynamics,representation alignment
6
+ Author: Miklós Králik, Sasha Tinelli, James Kozloski, Rui Ponte Costa, Andrew Sharott, Robert L. Peach
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
16
+ Requires-Dist: numpy>=1.26
17
+ Requires-Dist: opt-einsum>=3.4.0
18
+ Requires-Dist: torch>=2.9.1
19
+ Requires-Dist: tqdm>=4.67.1
20
+ Requires-Dist: markdown-it-py>=3.0 ; extra == 'docs'
21
+ Requires-Dist: matplotlib>=3.8 ; extra == 'examples'
22
+ Requires-Python: >=3.10
23
+ Project-URL: Documentation, https://mikloskralik.github.io/neuralign/
24
+ Project-URL: Repository, https://github.com/MiklosKralik/neuralign
25
+ Project-URL: Issues, https://github.com/MiklosKralik/neuralign/issues
26
+ Provides-Extra: docs
27
+ Provides-Extra: examples
28
+ Description-Content-Type: text/markdown
29
+
30
+ # <img src="https://raw.githubusercontent.com/MiklosKralik/neuralign/main/docs/assets/neuralign-mark.png" width="36" alt="" /> Neuralign
31
+
32
+ Find shared activity patterns across neural recordings, with a factor for each neuron and each trial.
33
+
34
+ [![Neuralign model overview](https://raw.githubusercontent.com/MiklosKralik/neuralign/main/docs/assets/neuralign-overview.png)](https://raw.githubusercontent.com/MiklosKralik/neuralign/main/docs/assets/neuralign-overview.pdf)
35
+
36
+ ## Install
37
+
38
+ Python 3.10 or later. From PyPI, after the first release:
39
+
40
+ ```bash
41
+ python -m pip install neuralign
42
+ ```
43
+
44
+ Or directly from GitHub:
45
+
46
+ ```bash
47
+ python -m pip install git+https://github.com/MiklosKralik/neuralign.git
48
+ ```
49
+
50
+ ## Quickstart
51
+
52
+ This example generates its own data:
53
+
54
+ ```python
55
+ from neuralign import NeuralignModel
56
+ from neuralign.simulation import simulate_sessions
57
+
58
+ sessions, _ = simulate_sessions(seed=42)
59
+ factors = NeuralignModel(sessions, rank=2, max_iter=500, seed=42).fit("cpu")
60
+
61
+ time_factors = factors.V # time × components
62
+ neuron_factors = factors[0].neuron_factors # neurons × components
63
+ trial_factors = factors[0].trial_factors # trials × components
64
+ ```
65
+
66
+ For your own recordings, pass a list of arrays in **neurons × time × trials** order. Sessions may have different neuron and trial counts, but must share time bins.
67
+
68
+ [Documentation](https://mikloskralik.github.io/neuralign/) · [Use your recordings](https://mikloskralik.github.io/neuralign/getting-started.html) · [Plotting example](https://github.com/MiklosKralik/neuralign/blob/main/examples/quickstart.py)
69
+
70
+ ## Paper
71
+
72
+ Neuralign is described in *Identifying aligned tensor decompositions across heterogeneous neural datasets*.
73
+
74
+ Please cite the software and manuscript when using Neuralign in research. [Citation](https://github.com/MiklosKralik/neuralign/blob/main/CITATION.cff) · [MIT License](https://github.com/MiklosKralik/neuralign/blob/main/LICENSE).
@@ -0,0 +1,45 @@
1
+ # <img src="https://raw.githubusercontent.com/MiklosKralik/neuralign/main/docs/assets/neuralign-mark.png" width="36" alt="" /> Neuralign
2
+
3
+ Find shared activity patterns across neural recordings, with a factor for each neuron and each trial.
4
+
5
+ [![Neuralign model overview](https://raw.githubusercontent.com/MiklosKralik/neuralign/main/docs/assets/neuralign-overview.png)](https://raw.githubusercontent.com/MiklosKralik/neuralign/main/docs/assets/neuralign-overview.pdf)
6
+
7
+ ## Install
8
+
9
+ Python 3.10 or later. From PyPI, after the first release:
10
+
11
+ ```bash
12
+ python -m pip install neuralign
13
+ ```
14
+
15
+ Or directly from GitHub:
16
+
17
+ ```bash
18
+ python -m pip install git+https://github.com/MiklosKralik/neuralign.git
19
+ ```
20
+
21
+ ## Quickstart
22
+
23
+ This example generates its own data:
24
+
25
+ ```python
26
+ from neuralign import NeuralignModel
27
+ from neuralign.simulation import simulate_sessions
28
+
29
+ sessions, _ = simulate_sessions(seed=42)
30
+ factors = NeuralignModel(sessions, rank=2, max_iter=500, seed=42).fit("cpu")
31
+
32
+ time_factors = factors.V # time × components
33
+ neuron_factors = factors[0].neuron_factors # neurons × components
34
+ trial_factors = factors[0].trial_factors # trials × components
35
+ ```
36
+
37
+ For your own recordings, pass a list of arrays in **neurons × time × trials** order. Sessions may have different neuron and trial counts, but must share time bins.
38
+
39
+ [Documentation](https://mikloskralik.github.io/neuralign/) · [Use your recordings](https://mikloskralik.github.io/neuralign/getting-started.html) · [Plotting example](https://github.com/MiklosKralik/neuralign/blob/main/examples/quickstart.py)
40
+
41
+ ## Paper
42
+
43
+ Neuralign is described in *Identifying aligned tensor decompositions across heterogeneous neural datasets*.
44
+
45
+ Please cite the software and manuscript when using Neuralign in research. [Citation](https://github.com/MiklosKralik/neuralign/blob/main/CITATION.cff) · [MIT License](https://github.com/MiklosKralik/neuralign/blob/main/LICENSE).
@@ -0,0 +1,133 @@
1
+ <!doctype html>
2
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
3
+ <title>API · Neuralign</title><link rel="icon" href="assets/neuralign-mark.svg" type="image/svg+xml"><link rel="stylesheet" href="style.css"></head>
4
+ <body><nav aria-label="Main navigation"><a href="index.html" >Neuralign</a> <a href="getting-started.html" >Your recordings</a> <a href="model-and-outputs.html" >Outputs</a> <a href="api.html" aria-current=page>API</a> <a href="reproducibility.html" >Save a fit</a></nav><main><h1>API reference</h1>
5
+ <pre><code class="language-python">from neuralign import NeuralignConfig, NeuralignModel, NeuralignFactors
6
+ </code></pre>
7
+ <h2>Fitting settings</h2>
8
+ <p>Pass settings through <code>NeuralignConfig</code> or directly to <code>NeuralignModel</code>.</p>
9
+ <table>
10
+ <thead>
11
+ <tr>
12
+ <th>Setting</th>
13
+ <th>Default</th>
14
+ <th>Purpose</th>
15
+ </tr>
16
+ </thead>
17
+ <tbody>
18
+ <tr>
19
+ <td><code>rank</code></td>
20
+ <td><code>4</code></td>
21
+ <td>Number of components</td>
22
+ </tr>
23
+ <tr>
24
+ <td><code>max_iter</code></td>
25
+ <td><code>2000</code></td>
26
+ <td>Maximum optimisation steps</td>
27
+ </tr>
28
+ <tr>
29
+ <td><code>lr</code></td>
30
+ <td><code>0.01</code></td>
31
+ <td>Learning rate</td>
32
+ </tr>
33
+ <tr>
34
+ <td><code>patience</code></td>
35
+ <td><code>100</code></td>
36
+ <td>Early-stopping patience</td>
37
+ </tr>
38
+ <tr>
39
+ <td><code>tol</code></td>
40
+ <td><code>1e-6</code></td>
41
+ <td>Early-stopping improvement threshold</td>
42
+ </tr>
43
+ <tr>
44
+ <td><code>seed</code></td>
45
+ <td><code>None</code></td>
46
+ <td>Random seed</td>
47
+ </tr>
48
+ <tr>
49
+ <td><code>progress</code></td>
50
+ <td><code>True</code></td>
51
+ <td>Display fitting progress</td>
52
+ </tr>
53
+ <tr>
54
+ <td><code>lambda_tensor_reconstruction</code></td>
55
+ <td><code>1.0</code></td>
56
+ <td>Reconstruction-loss weight</td>
57
+ </tr>
58
+ <tr>
59
+ <td><code>lambda_procrustes</code></td>
60
+ <td><code>1.0</code></td>
61
+ <td>Alignment-loss weight</td>
62
+ </tr>
63
+ <tr>
64
+ <td><code>non_negative</code></td>
65
+ <td><code>False</code></td>
66
+ <td>Nonnegative factors</td>
67
+ </tr>
68
+ <tr>
69
+ <td><code>loss_type</code></td>
70
+ <td><code>&quot;frobenius&quot;</code></td>
71
+ <td>Also <code>&quot;poisson&quot;</code> or <code>&quot;poisson_log&quot;</code></td>
72
+ </tr>
73
+ </tbody>
74
+ </table>
75
+ <p>The default Frobenius loss fits continuous activity. Count losses require nonnegative counts: <code>&quot;poisson&quot;</code> models rates and <code>&quot;poisson_log&quot;</code> models log-rates. For log-Poisson fits, exponentiate <code>reconstructed_tensor()</code> to obtain rates.</p>
76
+ <h2>NeuralignModel</h2>
77
+ <pre><code class="language-python">NeuralignModel(tensor_sequence, trial_labels=None, trial_mask=None,
78
+ config=None, **settings)
79
+ model.fit(device=&quot;cpu&quot;)
80
+ </code></pre>
81
+ <p><code>fit</code> returns <code>NeuralignFactors</code>. Inputs use neurons × time × trials and share a time axis.</p>
82
+ <p><code>trial_labels</code> provides one label sequence per session. Alignment uses trial means without labels and condition means with labels.</p>
83
+ <p><code>trial_mask</code> provides one Boolean array per session. <strong>True excludes a trial from alignment only:</strong> it still contributes to reconstruction. Each session needs an unmasked trial.</p>
84
+ <h2>NeuralignFactors</h2>
85
+ <table>
86
+ <thead>
87
+ <tr>
88
+ <th>Method</th>
89
+ <th>Behaviour</th>
90
+ </tr>
91
+ </thead>
92
+ <tbody>
93
+ <tr>
94
+ <td><code>len(factors)</code></td>
95
+ <td>Number of sessions</td>
96
+ </tr>
97
+ <tr>
98
+ <td><code>factors[i]</code> or <code>factors.session(i)</code></td>
99
+ <td>Session factors</td>
100
+ </tr>
101
+ <tr>
102
+ <td><code>factors.to(device)</code></td>
103
+ <td>Move tensors to a device</td>
104
+ </tr>
105
+ <tr>
106
+ <td><code>factors.save(path)</code></td>
107
+ <td>Save factors, configuration and metadata</td>
108
+ </tr>
109
+ <tr>
110
+ <td><code>NeuralignFactors.load(path, map_location=&quot;cpu&quot;)</code></td>
111
+ <td>Load a saved fit</td>
112
+ </tr>
113
+ <tr>
114
+ <td><code>factors.partial_fit(...)</code></td>
115
+ <td>Add sessions with shared factors frozen</td>
116
+ </tr>
117
+ </tbody>
118
+ </table>
119
+ <p><code>partial_fit(new_session, device=&quot;cpu&quot;, **settings)</code> returns a copy with new sessions appended. It fits their session factors while freezing <code>w</code>, <code>V</code>, <code>H</code> and condition prototypes. New sessions must share the fitted time axis. Recognised labels anchor alignment; sessions without recognised labels use trial means. Supervised adaptation applies a small condition-centroid penalty; <code>lambda_condition_centroid=0.0</code> disables it.</p>
120
+ <h2>Session outputs</h2>
121
+ <p>Access a session with <code>session = factors[i]</code>. See <a href="model-and-outputs.html">model outputs</a> for its factor properties and shapes.</p>
122
+ <pre><code class="language-python">session.reconstructed_tensor(components=None, *, normalized=False)
123
+ session.reconstructed_latent_tensor(components=None, *, normalized=False)
124
+ session.latent_trajectories(layout=&quot;ktr&quot;, *, components=None, normalized=False)
125
+ session.projected_trajectories(Z, layout=&quot;ktr&quot;)
126
+ </code></pre>
127
+ <p>For reconstruction methods, <code>components</code> accepts an index or a list of indices; <code>None</code> includes all components. <code>normalized=True</code> uses unit global weights.</p>
128
+ <h2>Simulation</h2>
129
+ <pre><code class="language-python">from neuralign.simulation import simulate_sessions
130
+ sessions, truth = simulate_sessions(n_sessions=4, n_time=64, noise_std=0.15, seed=42)
131
+ </code></pre>
132
+ <p>Generates rank-two recordings with different neuron/trial counts. <code>noise_std</code> is relative to the clean signal's standard deviation; <code>truth</code> contains the generating factors.</p>
133
+ </main></body></html>
@@ -0,0 +1,74 @@
1
+ # API reference
2
+
3
+ ```python
4
+ from neuralign import NeuralignConfig, NeuralignModel, NeuralignFactors
5
+ ```
6
+
7
+ ## Fitting settings
8
+
9
+ Pass settings through `NeuralignConfig` or directly to `NeuralignModel`.
10
+
11
+ | Setting | Default | Purpose |
12
+ | --- | --- | --- |
13
+ | `rank` | `4` | Number of components |
14
+ | `max_iter` | `2000` | Maximum optimisation steps |
15
+ | `lr` | `0.01` | Learning rate |
16
+ | `patience` | `100` | Early-stopping patience |
17
+ | `tol` | `1e-6` | Early-stopping improvement threshold |
18
+ | `seed` | `None` | Random seed |
19
+ | `progress` | `True` | Display fitting progress |
20
+ | `lambda_tensor_reconstruction` | `1.0` | Reconstruction-loss weight |
21
+ | `lambda_procrustes` | `1.0` | Alignment-loss weight |
22
+ | `non_negative` | `False` | Nonnegative factors |
23
+ | `loss_type` | `"frobenius"` | Also `"poisson"` or `"poisson_log"` |
24
+
25
+ The default Frobenius loss fits continuous activity. Count losses require nonnegative counts: `"poisson"` models rates and `"poisson_log"` models log-rates. For log-Poisson fits, exponentiate `reconstructed_tensor()` to obtain rates.
26
+
27
+ ## NeuralignModel
28
+
29
+ ```python
30
+ NeuralignModel(tensor_sequence, trial_labels=None, trial_mask=None,
31
+ config=None, **settings)
32
+ model.fit(device="cpu")
33
+ ```
34
+
35
+ `fit` returns `NeuralignFactors`. Inputs use neurons × time × trials and share a time axis.
36
+
37
+ `trial_labels` provides one label sequence per session. Alignment uses trial means without labels and condition means with labels.
38
+
39
+ `trial_mask` provides one Boolean array per session. **True excludes a trial from alignment only:** it still contributes to reconstruction. Each session needs an unmasked trial.
40
+
41
+ ## NeuralignFactors
42
+
43
+ | Method | Behaviour |
44
+ | --- | --- |
45
+ | `len(factors)` | Number of sessions |
46
+ | `factors[i]` or `factors.session(i)` | Session factors |
47
+ | `factors.to(device)` | Move tensors to a device |
48
+ | `factors.save(path)` | Save factors, configuration and metadata |
49
+ | `NeuralignFactors.load(path, map_location="cpu")` | Load a saved fit |
50
+ | `factors.partial_fit(...)` | Add sessions with shared factors frozen |
51
+
52
+ `partial_fit(new_session, device="cpu", **settings)` returns a copy with new sessions appended. It fits their session factors while freezing `w`, `V`, `H` and condition prototypes. New sessions must share the fitted time axis. Recognised labels anchor alignment; sessions without recognised labels use trial means. Supervised adaptation applies a small condition-centroid penalty; `lambda_condition_centroid=0.0` disables it.
53
+
54
+ ## Session outputs
55
+
56
+ Access a session with `session = factors[i]`. See [model outputs](model-and-outputs.md) for its factor properties and shapes.
57
+
58
+ ```python
59
+ session.reconstructed_tensor(components=None, *, normalized=False)
60
+ session.reconstructed_latent_tensor(components=None, *, normalized=False)
61
+ session.latent_trajectories(layout="ktr", *, components=None, normalized=False)
62
+ session.projected_trajectories(Z, layout="ktr")
63
+ ```
64
+
65
+ For reconstruction methods, `components` accepts an index or a list of indices; `None` includes all components. `normalized=True` uses unit global weights.
66
+
67
+ ## Simulation
68
+
69
+ ```python
70
+ from neuralign.simulation import simulate_sessions
71
+ sessions, truth = simulate_sessions(n_sessions=4, n_time=64, noise_std=0.15, seed=42)
72
+ ```
73
+
74
+ Generates rank-two recordings with different neuron/trial counts. `noise_std` is relative to the clean signal's standard deviation; `truth` contains the generating factors.