canns 0.13.1__py3-none-any.whl → 0.14.0__py3-none-any.whl
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.
- canns/analyzer/data/__init__.py +5 -1
- canns/analyzer/data/asa/__init__.py +27 -12
- canns/analyzer/data/asa/cohospace.py +336 -10
- canns/analyzer/data/asa/config.py +3 -0
- canns/analyzer/data/asa/embedding.py +48 -45
- canns/analyzer/data/asa/path.py +104 -2
- canns/analyzer/data/asa/plotting.py +88 -19
- canns/analyzer/data/asa/tda.py +11 -4
- canns/analyzer/data/cell_classification/__init__.py +97 -0
- canns/analyzer/data/cell_classification/core/__init__.py +26 -0
- canns/analyzer/data/cell_classification/core/grid_cells.py +633 -0
- canns/analyzer/data/cell_classification/core/grid_modules_leiden.py +288 -0
- canns/analyzer/data/cell_classification/core/head_direction.py +347 -0
- canns/analyzer/data/cell_classification/core/spatial_analysis.py +431 -0
- canns/analyzer/data/cell_classification/io/__init__.py +5 -0
- canns/analyzer/data/cell_classification/io/matlab_loader.py +417 -0
- canns/analyzer/data/cell_classification/utils/__init__.py +39 -0
- canns/analyzer/data/cell_classification/utils/circular_stats.py +383 -0
- canns/analyzer/data/cell_classification/utils/correlation.py +318 -0
- canns/analyzer/data/cell_classification/utils/geometry.py +442 -0
- canns/analyzer/data/cell_classification/utils/image_processing.py +416 -0
- canns/analyzer/data/cell_classification/visualization/__init__.py +19 -0
- canns/analyzer/data/cell_classification/visualization/grid_plots.py +292 -0
- canns/analyzer/data/cell_classification/visualization/hd_plots.py +200 -0
- canns/analyzer/metrics/__init__.py +2 -1
- canns/analyzer/visualization/core/config.py +46 -4
- canns/data/__init__.py +6 -1
- canns/data/datasets.py +154 -1
- canns/data/loaders.py +37 -0
- canns/pipeline/__init__.py +13 -9
- canns/pipeline/__main__.py +6 -0
- canns/pipeline/asa/runner.py +105 -41
- canns/pipeline/asa_gui/__init__.py +68 -0
- canns/pipeline/asa_gui/__main__.py +6 -0
- canns/pipeline/asa_gui/analysis_modes/__init__.py +42 -0
- canns/pipeline/asa_gui/analysis_modes/base.py +39 -0
- canns/pipeline/asa_gui/analysis_modes/batch_mode.py +21 -0
- canns/pipeline/asa_gui/analysis_modes/cohomap_mode.py +56 -0
- canns/pipeline/asa_gui/analysis_modes/cohospace_mode.py +194 -0
- canns/pipeline/asa_gui/analysis_modes/decode_mode.py +52 -0
- canns/pipeline/asa_gui/analysis_modes/fr_mode.py +81 -0
- canns/pipeline/asa_gui/analysis_modes/frm_mode.py +92 -0
- canns/pipeline/asa_gui/analysis_modes/gridscore_mode.py +123 -0
- canns/pipeline/asa_gui/analysis_modes/pathcompare_mode.py +199 -0
- canns/pipeline/asa_gui/analysis_modes/tda_mode.py +112 -0
- canns/pipeline/asa_gui/app.py +29 -0
- canns/pipeline/asa_gui/controllers/__init__.py +6 -0
- canns/pipeline/asa_gui/controllers/analysis_controller.py +59 -0
- canns/pipeline/asa_gui/controllers/preprocess_controller.py +89 -0
- canns/pipeline/asa_gui/core/__init__.py +15 -0
- canns/pipeline/asa_gui/core/cache.py +14 -0
- canns/pipeline/asa_gui/core/runner.py +1936 -0
- canns/pipeline/asa_gui/core/state.py +324 -0
- canns/pipeline/asa_gui/core/worker.py +260 -0
- canns/pipeline/asa_gui/main_window.py +184 -0
- canns/pipeline/asa_gui/models/__init__.py +7 -0
- canns/pipeline/asa_gui/models/config.py +14 -0
- canns/pipeline/asa_gui/models/job.py +31 -0
- canns/pipeline/asa_gui/models/presets.py +21 -0
- canns/pipeline/asa_gui/resources/__init__.py +16 -0
- canns/pipeline/asa_gui/resources/dark.qss +167 -0
- canns/pipeline/asa_gui/resources/light.qss +163 -0
- canns/pipeline/asa_gui/resources/styles.qss +130 -0
- canns/pipeline/asa_gui/utils/__init__.py +1 -0
- canns/pipeline/asa_gui/utils/formatters.py +15 -0
- canns/pipeline/asa_gui/utils/io_adapters.py +40 -0
- canns/pipeline/asa_gui/utils/validators.py +41 -0
- canns/pipeline/asa_gui/views/__init__.py +1 -0
- canns/pipeline/asa_gui/views/help_content.py +171 -0
- canns/pipeline/asa_gui/views/pages/__init__.py +6 -0
- canns/pipeline/asa_gui/views/pages/analysis_page.py +565 -0
- canns/pipeline/asa_gui/views/pages/preprocess_page.py +492 -0
- canns/pipeline/asa_gui/views/panels/__init__.py +1 -0
- canns/pipeline/asa_gui/views/widgets/__init__.py +21 -0
- canns/pipeline/asa_gui/views/widgets/artifacts_tab.py +44 -0
- canns/pipeline/asa_gui/views/widgets/drop_zone.py +80 -0
- canns/pipeline/asa_gui/views/widgets/file_list.py +27 -0
- canns/pipeline/asa_gui/views/widgets/gridscore_tab.py +308 -0
- canns/pipeline/asa_gui/views/widgets/help_dialog.py +27 -0
- canns/pipeline/asa_gui/views/widgets/image_tab.py +50 -0
- canns/pipeline/asa_gui/views/widgets/image_viewer.py +97 -0
- canns/pipeline/asa_gui/views/widgets/log_box.py +16 -0
- canns/pipeline/asa_gui/views/widgets/pathcompare_tab.py +200 -0
- canns/pipeline/asa_gui/views/widgets/popup_combo.py +25 -0
- canns/pipeline/gallery/__init__.py +15 -5
- canns/pipeline/gallery/__main__.py +11 -0
- canns/pipeline/gallery/app.py +705 -0
- canns/pipeline/gallery/runner.py +790 -0
- canns/pipeline/gallery/state.py +51 -0
- canns/pipeline/gallery/styles.tcss +123 -0
- canns/pipeline/launcher.py +81 -0
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/METADATA +11 -1
- canns-0.14.0.dist-info/RECORD +163 -0
- canns-0.14.0.dist-info/entry_points.txt +5 -0
- canns/pipeline/_base.py +0 -50
- canns-0.13.1.dist-info/RECORD +0 -89
- canns-0.13.1.dist-info/entry_points.txt +0 -3
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/WHEEL +0 -0
- {canns-0.13.1.dist-info → canns-0.14.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""CohoMap analysis mode."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from PySide6.QtWidgets import QCheckBox, QFormLayout, QGroupBox, QSpinBox
|
|
6
|
+
|
|
7
|
+
from ..views.widgets.popup_combo import PopupComboBox
|
|
8
|
+
from .base import AbstractAnalysisMode, configure_form_layout
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class CohoMapMode(AbstractAnalysisMode):
|
|
12
|
+
name = "cohomap"
|
|
13
|
+
display_name = "CohoMap (TDA + decode)"
|
|
14
|
+
|
|
15
|
+
def create_params_widget(self) -> QGroupBox:
|
|
16
|
+
box = QGroupBox("CohoMap Parameters")
|
|
17
|
+
form = QFormLayout(box)
|
|
18
|
+
configure_form_layout(form)
|
|
19
|
+
|
|
20
|
+
self.decode_version = PopupComboBox()
|
|
21
|
+
self.decode_version.addItems(["v2", "v0"])
|
|
22
|
+
|
|
23
|
+
self.num_circ = QSpinBox()
|
|
24
|
+
self.num_circ.setRange(1, 50)
|
|
25
|
+
self.num_circ.setValue(2)
|
|
26
|
+
|
|
27
|
+
self.real_ground = QCheckBox()
|
|
28
|
+
self.real_ground.setChecked(True)
|
|
29
|
+
|
|
30
|
+
self.real_of = QCheckBox()
|
|
31
|
+
self.real_of.setChecked(True)
|
|
32
|
+
|
|
33
|
+
self.subsample = QSpinBox()
|
|
34
|
+
self.subsample.setRange(1, 5000)
|
|
35
|
+
self.subsample.setValue(10)
|
|
36
|
+
|
|
37
|
+
form.addRow("Decode version", self.decode_version)
|
|
38
|
+
form.addRow("Decode num_circ", self.num_circ)
|
|
39
|
+
form.addRow("CohoMap subsample", self.subsample)
|
|
40
|
+
|
|
41
|
+
return box
|
|
42
|
+
|
|
43
|
+
def collect_params(self) -> dict:
|
|
44
|
+
return {
|
|
45
|
+
"decode_version": str(self.decode_version.currentText()),
|
|
46
|
+
"num_circ": int(self.num_circ.value()),
|
|
47
|
+
"real_ground": bool(self.real_ground.isChecked()),
|
|
48
|
+
"real_of": bool(self.real_of.isChecked()),
|
|
49
|
+
"cohomap_subsample": int(self.subsample.value()),
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def apply_preset(self, preset: str) -> None:
|
|
53
|
+
if preset == "grid":
|
|
54
|
+
self.num_circ.setValue(2)
|
|
55
|
+
elif preset == "hd":
|
|
56
|
+
self.num_circ.setValue(1)
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"""CohoSpace analysis mode."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from PySide6.QtWidgets import (
|
|
6
|
+
QCheckBox,
|
|
7
|
+
QDoubleSpinBox,
|
|
8
|
+
QFormLayout,
|
|
9
|
+
QGroupBox,
|
|
10
|
+
QHBoxLayout,
|
|
11
|
+
QLabel,
|
|
12
|
+
QPushButton,
|
|
13
|
+
QSpinBox,
|
|
14
|
+
QWidget,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from ..views.widgets.popup_combo import PopupComboBox
|
|
18
|
+
from .base import AbstractAnalysisMode, configure_form_layout
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CohoSpaceMode(AbstractAnalysisMode):
|
|
22
|
+
name = "cohospace"
|
|
23
|
+
display_name = "CohoSpace / CohoScore"
|
|
24
|
+
|
|
25
|
+
def create_params_widget(self) -> QGroupBox:
|
|
26
|
+
box = QGroupBox("CohoSpace / CohoScore")
|
|
27
|
+
form = QFormLayout(box)
|
|
28
|
+
configure_form_layout(form)
|
|
29
|
+
|
|
30
|
+
self.dim_mode = PopupComboBox()
|
|
31
|
+
self.dim_mode.addItem("2D", userData="2d")
|
|
32
|
+
self.dim_mode.addItem("1D", userData="1d")
|
|
33
|
+
self.dim_mode.setCurrentIndex(0)
|
|
34
|
+
|
|
35
|
+
self.dim = QSpinBox()
|
|
36
|
+
self.dim.setRange(1, 10)
|
|
37
|
+
self.dim.setValue(1)
|
|
38
|
+
|
|
39
|
+
self.dim1 = QSpinBox()
|
|
40
|
+
self.dim1.setRange(1, 10)
|
|
41
|
+
self.dim1.setValue(1)
|
|
42
|
+
|
|
43
|
+
self.dim2 = QSpinBox()
|
|
44
|
+
self.dim2.setRange(1, 10)
|
|
45
|
+
self.dim2.setValue(2)
|
|
46
|
+
|
|
47
|
+
self.mode = PopupComboBox()
|
|
48
|
+
self.mode.addItems(["spike", "fr"])
|
|
49
|
+
self.mode.setCurrentText("spike")
|
|
50
|
+
|
|
51
|
+
self.top_percent = QDoubleSpinBox()
|
|
52
|
+
self.top_percent.setRange(0.1, 50.0)
|
|
53
|
+
self.top_percent.setSingleStep(0.5)
|
|
54
|
+
self.top_percent.setValue(2.0)
|
|
55
|
+
|
|
56
|
+
self.view = PopupComboBox()
|
|
57
|
+
self.view.addItem("single neuron", userData="single")
|
|
58
|
+
self.view.addItem("all neurons (aggregate)", userData="population")
|
|
59
|
+
self.view.setCurrentIndex(0)
|
|
60
|
+
|
|
61
|
+
self.subsample = QSpinBox()
|
|
62
|
+
self.subsample.setRange(1, 100)
|
|
63
|
+
self.subsample.setValue(2)
|
|
64
|
+
|
|
65
|
+
self.unfold = PopupComboBox()
|
|
66
|
+
self.unfold.addItems(["square", "skew"])
|
|
67
|
+
|
|
68
|
+
self.skew_show_grid = QCheckBox("Skew: show grid")
|
|
69
|
+
self.skew_show_grid.setChecked(True)
|
|
70
|
+
|
|
71
|
+
self.skew_tiles = QSpinBox()
|
|
72
|
+
self.skew_tiles.setRange(0, 10)
|
|
73
|
+
self.skew_tiles.setValue(0)
|
|
74
|
+
|
|
75
|
+
self.top_k = QSpinBox()
|
|
76
|
+
self.top_k.setRange(1, 10_000_000)
|
|
77
|
+
self.top_k.setValue(10)
|
|
78
|
+
|
|
79
|
+
self.neuron_id = QSpinBox()
|
|
80
|
+
self.neuron_id.setRange(0, 1_000_000)
|
|
81
|
+
self.neuron_id.setValue(0)
|
|
82
|
+
|
|
83
|
+
self.btn_prev = QPushButton("←")
|
|
84
|
+
self.btn_next = QPushButton("→")
|
|
85
|
+
self.btn_show = QPushButton("Show")
|
|
86
|
+
self._neuron_row = QWidget()
|
|
87
|
+
neuron_row_layout = QHBoxLayout(self._neuron_row)
|
|
88
|
+
neuron_row_layout.setContentsMargins(0, 0, 0, 0)
|
|
89
|
+
neuron_row_layout.addWidget(self.btn_prev)
|
|
90
|
+
neuron_row_layout.addWidget(self.neuron_id, 1)
|
|
91
|
+
neuron_row_layout.addWidget(self.btn_next)
|
|
92
|
+
neuron_row_layout.addSpacing(8)
|
|
93
|
+
neuron_row_layout.addWidget(self.btn_show)
|
|
94
|
+
|
|
95
|
+
self.enable_score = QCheckBox("Compute CohoScore & top-K")
|
|
96
|
+
self.enable_score.setChecked(True)
|
|
97
|
+
|
|
98
|
+
self.use_best = QCheckBox("Use best neuron by CohoScore")
|
|
99
|
+
self.use_best.setChecked(True)
|
|
100
|
+
|
|
101
|
+
form.addRow("Dim mode", self.dim_mode)
|
|
102
|
+
|
|
103
|
+
self._dims1d_label = QLabel("Dim")
|
|
104
|
+
self._dims1d_wrap = QWidget()
|
|
105
|
+
dims_1d_layout = QHBoxLayout(self._dims1d_wrap)
|
|
106
|
+
dims_1d_layout.setContentsMargins(0, 0, 0, 0)
|
|
107
|
+
dims_1d_layout.addWidget(self.dim)
|
|
108
|
+
dims_1d_layout.addStretch(1)
|
|
109
|
+
|
|
110
|
+
self._dims2d_label = QLabel("Dim X / Dim Y")
|
|
111
|
+
self._dims2d_wrap = QWidget()
|
|
112
|
+
dims_2d_layout = QHBoxLayout(self._dims2d_wrap)
|
|
113
|
+
dims_2d_layout.setContentsMargins(0, 0, 0, 0)
|
|
114
|
+
dims_2d_layout.addWidget(QLabel("dim1"))
|
|
115
|
+
dims_2d_layout.addWidget(self.dim1)
|
|
116
|
+
dims_2d_layout.addSpacing(8)
|
|
117
|
+
dims_2d_layout.addWidget(QLabel("dim2"))
|
|
118
|
+
dims_2d_layout.addWidget(self.dim2)
|
|
119
|
+
dims_2d_layout.addStretch(1)
|
|
120
|
+
|
|
121
|
+
form.addRow(self._dims1d_label, self._dims1d_wrap)
|
|
122
|
+
form.addRow(self._dims2d_label, self._dims2d_wrap)
|
|
123
|
+
form.addRow("Unfold", self.unfold)
|
|
124
|
+
form.addRow("", self.skew_show_grid)
|
|
125
|
+
form.addRow("Skew tiles", self.skew_tiles)
|
|
126
|
+
form.addRow("Mode (spike / fr)", self.mode)
|
|
127
|
+
form.addRow("Top % (threshold)", self.top_percent)
|
|
128
|
+
form.addRow(self.enable_score)
|
|
129
|
+
form.addRow("View", self.view)
|
|
130
|
+
form.addRow("Top-K neurons", self.top_k)
|
|
131
|
+
form.addRow("Neuron id", self._neuron_row)
|
|
132
|
+
form.addRow("", self.use_best)
|
|
133
|
+
|
|
134
|
+
self.dim_mode.currentIndexChanged.connect(self._refresh_dim_mode)
|
|
135
|
+
self.view.currentIndexChanged.connect(self._refresh_view)
|
|
136
|
+
self.enable_score.toggled.connect(self._refresh_view)
|
|
137
|
+
self.btn_prev.clicked.connect(lambda: self._shift(-1))
|
|
138
|
+
self.btn_next.clicked.connect(lambda: self._shift(+1))
|
|
139
|
+
self._refresh_dim_mode()
|
|
140
|
+
self._refresh_view()
|
|
141
|
+
|
|
142
|
+
return box
|
|
143
|
+
|
|
144
|
+
def collect_params(self) -> dict:
|
|
145
|
+
return {
|
|
146
|
+
"dim_mode": str(self.dim_mode.currentData() or "2d"),
|
|
147
|
+
"dim": int(self.dim.value()),
|
|
148
|
+
"dim1": int(self.dim1.value()),
|
|
149
|
+
"dim2": int(self.dim2.value()),
|
|
150
|
+
"mode": str(self.mode.currentText()),
|
|
151
|
+
"top_percent": float(self.top_percent.value()),
|
|
152
|
+
"view": str(self.view.currentData() or "single"),
|
|
153
|
+
"subsample": int(self.subsample.value()),
|
|
154
|
+
"unfold": str(self.unfold.currentText()),
|
|
155
|
+
"skew_show_grid": bool(self.skew_show_grid.isChecked()),
|
|
156
|
+
"skew_tiles": int(self.skew_tiles.value()),
|
|
157
|
+
"neuron_id": int(self.neuron_id.value()),
|
|
158
|
+
"enable_score": bool(self.enable_score.isChecked()),
|
|
159
|
+
"top_k": int(self.top_k.value()),
|
|
160
|
+
"use_best": bool(self.use_best.isChecked()),
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def apply_ranges(self, neuron_count: int | None, total_steps: int | None) -> None:
|
|
164
|
+
if neuron_count is None:
|
|
165
|
+
return
|
|
166
|
+
self.neuron_id.setRange(0, max(0, neuron_count - 1))
|
|
167
|
+
self.top_k.setMaximum(max(1, neuron_count))
|
|
168
|
+
if self.top_k.value() <= 0:
|
|
169
|
+
self.top_k.setValue(min(10, neuron_count))
|
|
170
|
+
|
|
171
|
+
def _refresh_dim_mode(self) -> None:
|
|
172
|
+
mode = str(self.dim_mode.currentData() or "2d")
|
|
173
|
+
is_1d = mode == "1d"
|
|
174
|
+
self._dims1d_label.setVisible(is_1d)
|
|
175
|
+
self._dims1d_wrap.setVisible(is_1d)
|
|
176
|
+
self._dims2d_label.setVisible(not is_1d)
|
|
177
|
+
self._dims2d_wrap.setVisible(not is_1d)
|
|
178
|
+
self.unfold.setEnabled(not is_1d)
|
|
179
|
+
self.skew_show_grid.setEnabled(not is_1d)
|
|
180
|
+
self.skew_tiles.setEnabled(not is_1d)
|
|
181
|
+
|
|
182
|
+
def _refresh_view(self) -> None:
|
|
183
|
+
view_single = str(self.view.currentData() or "single") == "single"
|
|
184
|
+
self.neuron_id.setEnabled(view_single)
|
|
185
|
+
self.btn_prev.setEnabled(view_single)
|
|
186
|
+
self.btn_next.setEnabled(view_single)
|
|
187
|
+
self.btn_show.setEnabled(view_single)
|
|
188
|
+
self.use_best.setEnabled(bool(self.enable_score.isChecked()))
|
|
189
|
+
self.top_k.setEnabled(bool(self.enable_score.isChecked()))
|
|
190
|
+
|
|
191
|
+
def _shift(self, delta: int) -> None:
|
|
192
|
+
val = self.neuron_id.value() + int(delta)
|
|
193
|
+
val = max(self.neuron_id.minimum(), min(self.neuron_id.maximum(), val))
|
|
194
|
+
self.neuron_id.setValue(val)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Decode analysis mode."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from PySide6.QtWidgets import QCheckBox, QFormLayout, QGroupBox, QSpinBox
|
|
6
|
+
|
|
7
|
+
from ..views.widgets.popup_combo import PopupComboBox
|
|
8
|
+
from .base import AbstractAnalysisMode, configure_form_layout
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DecodeMode(AbstractAnalysisMode):
|
|
12
|
+
name = "decode"
|
|
13
|
+
display_name = "Decode"
|
|
14
|
+
|
|
15
|
+
def create_params_widget(self) -> QGroupBox:
|
|
16
|
+
box = QGroupBox("Decode Parameters")
|
|
17
|
+
form = QFormLayout(box)
|
|
18
|
+
configure_form_layout(form)
|
|
19
|
+
|
|
20
|
+
self.decode_version = PopupComboBox()
|
|
21
|
+
self.decode_version.addItems(["v2", "v0"])
|
|
22
|
+
|
|
23
|
+
self.num_circ = QSpinBox()
|
|
24
|
+
self.num_circ.setRange(1, 10)
|
|
25
|
+
self.num_circ.setValue(2)
|
|
26
|
+
|
|
27
|
+
self.real_ground = QCheckBox()
|
|
28
|
+
self.real_ground.setChecked(True)
|
|
29
|
+
|
|
30
|
+
self.real_of = QCheckBox()
|
|
31
|
+
self.real_of.setChecked(True)
|
|
32
|
+
|
|
33
|
+
form.addRow("decode_version", self.decode_version)
|
|
34
|
+
form.addRow("num_circ", self.num_circ)
|
|
35
|
+
form.addRow("real_ground", self.real_ground)
|
|
36
|
+
form.addRow("real_of", self.real_of)
|
|
37
|
+
|
|
38
|
+
return box
|
|
39
|
+
|
|
40
|
+
def collect_params(self) -> dict:
|
|
41
|
+
return {
|
|
42
|
+
"decode_version": str(self.decode_version.currentText()),
|
|
43
|
+
"num_circ": int(self.num_circ.value()),
|
|
44
|
+
"real_ground": bool(self.real_ground.isChecked()),
|
|
45
|
+
"real_of": bool(self.real_of.isChecked()),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def apply_preset(self, preset: str) -> None:
|
|
49
|
+
if preset == "grid":
|
|
50
|
+
self.num_circ.setValue(2)
|
|
51
|
+
elif preset == "hd":
|
|
52
|
+
self.num_circ.setValue(1)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""FR analysis mode."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from PySide6.QtWidgets import QFormLayout, QGroupBox, QSpinBox
|
|
6
|
+
|
|
7
|
+
from ..views.widgets.popup_combo import PopupComboBox
|
|
8
|
+
from .base import AbstractAnalysisMode, configure_form_layout
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FRMode(AbstractAnalysisMode):
|
|
12
|
+
name = "fr"
|
|
13
|
+
display_name = "FR Heatmap"
|
|
14
|
+
|
|
15
|
+
def create_params_widget(self) -> QGroupBox:
|
|
16
|
+
box = QGroupBox("FR (population heatmap)")
|
|
17
|
+
form = QFormLayout(box)
|
|
18
|
+
configure_form_layout(form)
|
|
19
|
+
|
|
20
|
+
self.neuron_start = QSpinBox()
|
|
21
|
+
self.neuron_start.setRange(0, 1_000_000)
|
|
22
|
+
self.neuron_start.setValue(0)
|
|
23
|
+
|
|
24
|
+
self.neuron_end = QSpinBox()
|
|
25
|
+
self.neuron_end.setRange(0, 1_000_000)
|
|
26
|
+
self.neuron_end.setValue(0)
|
|
27
|
+
|
|
28
|
+
self.time_start = QSpinBox()
|
|
29
|
+
self.time_start.setRange(0, 10_000_000)
|
|
30
|
+
self.time_start.setValue(0)
|
|
31
|
+
|
|
32
|
+
self.time_end = QSpinBox()
|
|
33
|
+
self.time_end.setRange(0, 10_000_000)
|
|
34
|
+
self.time_end.setValue(0)
|
|
35
|
+
|
|
36
|
+
self.normalize = PopupComboBox()
|
|
37
|
+
self.normalize.addItems(["none", "zscore_per_neuron", "minmax_per_neuron"])
|
|
38
|
+
self.normalize.setCurrentText("none")
|
|
39
|
+
|
|
40
|
+
self.mode = PopupComboBox()
|
|
41
|
+
self.mode.addItems(["fr", "spike"])
|
|
42
|
+
self.mode.setToolTip("Use 'fr' for firing-rate matrix (requires preprocessing).")
|
|
43
|
+
|
|
44
|
+
form.addRow("FR neuron_start", self.neuron_start)
|
|
45
|
+
form.addRow("FR neuron_end", self.neuron_end)
|
|
46
|
+
form.addRow("FR t_start", self.time_start)
|
|
47
|
+
form.addRow("FR t_end", self.time_end)
|
|
48
|
+
form.addRow("FR mode", self.mode)
|
|
49
|
+
form.addRow("FR normalize", self.normalize)
|
|
50
|
+
|
|
51
|
+
return box
|
|
52
|
+
|
|
53
|
+
def collect_params(self) -> dict:
|
|
54
|
+
neuron_start = int(self.neuron_start.value())
|
|
55
|
+
neuron_end = int(self.neuron_end.value())
|
|
56
|
+
time_start = int(self.time_start.value())
|
|
57
|
+
time_end = int(self.time_end.value())
|
|
58
|
+
neuron_range = None
|
|
59
|
+
time_range = None
|
|
60
|
+
if neuron_end > neuron_start:
|
|
61
|
+
neuron_range = (neuron_start, neuron_end)
|
|
62
|
+
if time_end > time_start:
|
|
63
|
+
time_range = (time_start, time_end)
|
|
64
|
+
return {
|
|
65
|
+
"neuron_range": neuron_range,
|
|
66
|
+
"time_range": time_range,
|
|
67
|
+
"normalize": str(self.normalize.currentText()),
|
|
68
|
+
"mode": str(self.mode.currentText()),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
def apply_ranges(self, neuron_count: int | None, total_steps: int | None) -> None:
|
|
72
|
+
if neuron_count is not None:
|
|
73
|
+
self.neuron_start.setRange(0, max(0, neuron_count - 1))
|
|
74
|
+
self.neuron_end.setRange(0, neuron_count)
|
|
75
|
+
if self.neuron_end.value() == 0:
|
|
76
|
+
self.neuron_end.setValue(neuron_count)
|
|
77
|
+
if total_steps is not None:
|
|
78
|
+
self.time_start.setRange(0, max(0, total_steps - 1))
|
|
79
|
+
self.time_end.setRange(0, total_steps)
|
|
80
|
+
if self.time_end.value() == 0:
|
|
81
|
+
self.time_end.setValue(total_steps)
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""FRM analysis mode."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from PySide6.QtWidgets import (
|
|
6
|
+
QCheckBox,
|
|
7
|
+
QDoubleSpinBox,
|
|
8
|
+
QFormLayout,
|
|
9
|
+
QGroupBox,
|
|
10
|
+
QHBoxLayout,
|
|
11
|
+
QPushButton,
|
|
12
|
+
QSpinBox,
|
|
13
|
+
QWidget,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from ..views.widgets.popup_combo import PopupComboBox
|
|
17
|
+
from .base import AbstractAnalysisMode, configure_form_layout
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FRMMode(AbstractAnalysisMode):
|
|
21
|
+
name = "frm"
|
|
22
|
+
display_name = "FRM (single neuron)"
|
|
23
|
+
|
|
24
|
+
def create_params_widget(self) -> QGroupBox:
|
|
25
|
+
box = QGroupBox("FRM (single neuron)")
|
|
26
|
+
form = QFormLayout(box)
|
|
27
|
+
configure_form_layout(form)
|
|
28
|
+
|
|
29
|
+
self.neuron_id = QSpinBox()
|
|
30
|
+
self.neuron_id.setRange(0, 1_000_000)
|
|
31
|
+
self.neuron_id.setValue(0)
|
|
32
|
+
|
|
33
|
+
self.bin_size = QSpinBox()
|
|
34
|
+
self.bin_size.setRange(5, 500)
|
|
35
|
+
self.bin_size.setValue(50)
|
|
36
|
+
|
|
37
|
+
self.min_occupancy = QSpinBox()
|
|
38
|
+
self.min_occupancy.setRange(1, 10_000)
|
|
39
|
+
self.min_occupancy.setValue(1)
|
|
40
|
+
|
|
41
|
+
self.smoothing = QCheckBox()
|
|
42
|
+
self.smoothing.setChecked(False)
|
|
43
|
+
|
|
44
|
+
self.smooth_sigma = QDoubleSpinBox()
|
|
45
|
+
self.smooth_sigma.setRange(0.1, 50.0)
|
|
46
|
+
self.smooth_sigma.setSingleStep(0.1)
|
|
47
|
+
self.smooth_sigma.setValue(1.0)
|
|
48
|
+
|
|
49
|
+
self.mode = PopupComboBox()
|
|
50
|
+
self.mode.addItems(["fr", "spike"])
|
|
51
|
+
self.mode.setToolTip("Use 'fr' for firing-rate matrix (requires preprocessing).")
|
|
52
|
+
|
|
53
|
+
self.btn_prev = QPushButton("←")
|
|
54
|
+
self.btn_next = QPushButton("→")
|
|
55
|
+
neuron_row = QWidget()
|
|
56
|
+
neuron_row_layout = QHBoxLayout(neuron_row)
|
|
57
|
+
neuron_row_layout.setContentsMargins(0, 0, 0, 0)
|
|
58
|
+
neuron_row_layout.addWidget(self.btn_prev)
|
|
59
|
+
neuron_row_layout.addWidget(self.neuron_id, 1)
|
|
60
|
+
neuron_row_layout.addWidget(self.btn_next)
|
|
61
|
+
|
|
62
|
+
self.btn_prev.clicked.connect(lambda: self._shift(-1))
|
|
63
|
+
self.btn_next.clicked.connect(lambda: self._shift(+1))
|
|
64
|
+
|
|
65
|
+
form.addRow("FRM neuron_id", neuron_row)
|
|
66
|
+
form.addRow("FRM bins", self.bin_size)
|
|
67
|
+
form.addRow("FRM min_occupancy", self.min_occupancy)
|
|
68
|
+
form.addRow("FRM smoothing", self.smoothing)
|
|
69
|
+
form.addRow("FRM sigma", self.smooth_sigma)
|
|
70
|
+
form.addRow("FRM mode", self.mode)
|
|
71
|
+
|
|
72
|
+
return box
|
|
73
|
+
|
|
74
|
+
def collect_params(self) -> dict:
|
|
75
|
+
return {
|
|
76
|
+
"neuron_id": int(self.neuron_id.value()),
|
|
77
|
+
"bin_size": int(self.bin_size.value()),
|
|
78
|
+
"min_occupancy": int(self.min_occupancy.value()),
|
|
79
|
+
"smoothing": bool(self.smoothing.isChecked()),
|
|
80
|
+
"smooth_sigma": float(self.smooth_sigma.value()),
|
|
81
|
+
"mode": str(self.mode.currentText()),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
def apply_ranges(self, neuron_count: int | None, total_steps: int | None) -> None:
|
|
85
|
+
if neuron_count is None:
|
|
86
|
+
return
|
|
87
|
+
self.neuron_id.setRange(0, max(0, neuron_count - 1))
|
|
88
|
+
|
|
89
|
+
def _shift(self, delta: int) -> None:
|
|
90
|
+
val = self.neuron_id.value() + int(delta)
|
|
91
|
+
val = max(self.neuron_id.minimum(), min(self.neuron_id.maximum(), val))
|
|
92
|
+
self.neuron_id.setValue(val)
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""GridScore analysis modes."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from PySide6.QtWidgets import QCheckBox, QDoubleSpinBox, QFormLayout, QGroupBox, QSpinBox
|
|
8
|
+
|
|
9
|
+
from ..views.widgets.popup_combo import PopupComboBox
|
|
10
|
+
from .base import AbstractAnalysisMode, configure_form_layout
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GridScoreMode(AbstractAnalysisMode):
|
|
14
|
+
name = "gridscore"
|
|
15
|
+
display_name = "Grid Score (classic)"
|
|
16
|
+
|
|
17
|
+
def create_params_widget(self) -> QGroupBox:
|
|
18
|
+
box = QGroupBox("Grid Score (classic)")
|
|
19
|
+
form = QFormLayout(box)
|
|
20
|
+
configure_form_layout(form)
|
|
21
|
+
|
|
22
|
+
self.neuron_start = QSpinBox()
|
|
23
|
+
self.neuron_start.setRange(0, 10_000_000)
|
|
24
|
+
self.neuron_start.setValue(0)
|
|
25
|
+
|
|
26
|
+
self.neuron_end = QSpinBox()
|
|
27
|
+
self.neuron_end.setRange(0, 10_000_000)
|
|
28
|
+
self.neuron_end.setValue(0)
|
|
29
|
+
|
|
30
|
+
self.bins = QSpinBox()
|
|
31
|
+
self.bins.setRange(5, 500)
|
|
32
|
+
self.bins.setValue(50)
|
|
33
|
+
|
|
34
|
+
self.min_occupancy = QSpinBox()
|
|
35
|
+
self.min_occupancy.setRange(1, 10_000)
|
|
36
|
+
self.min_occupancy.setValue(1)
|
|
37
|
+
|
|
38
|
+
self.smoothing = QCheckBox()
|
|
39
|
+
self.smoothing.setChecked(False)
|
|
40
|
+
|
|
41
|
+
self.sigma = QDoubleSpinBox()
|
|
42
|
+
self.sigma.setRange(0.1, 50.0)
|
|
43
|
+
self.sigma.setSingleStep(0.1)
|
|
44
|
+
self.sigma.setValue(1.0)
|
|
45
|
+
|
|
46
|
+
self.overlap = QDoubleSpinBox()
|
|
47
|
+
self.overlap.setRange(0.1, 1.0)
|
|
48
|
+
self.overlap.setSingleStep(0.05)
|
|
49
|
+
self.overlap.setValue(0.8)
|
|
50
|
+
|
|
51
|
+
self.mode = PopupComboBox()
|
|
52
|
+
self.mode.addItems(["fr", "spike"])
|
|
53
|
+
self.mode.setToolTip("Use 'fr' for firing-rate maps (requires preprocessing).")
|
|
54
|
+
|
|
55
|
+
self.score_thr = QDoubleSpinBox()
|
|
56
|
+
self.score_thr.setRange(-2.0, 2.0)
|
|
57
|
+
self.score_thr.setSingleStep(0.05)
|
|
58
|
+
self.score_thr.setValue(0.3)
|
|
59
|
+
|
|
60
|
+
self.neuron_id = QSpinBox()
|
|
61
|
+
self.neuron_id.setRange(0, 10_000_000)
|
|
62
|
+
self.neuron_id.setValue(0)
|
|
63
|
+
|
|
64
|
+
form.addRow("neuron_start", self.neuron_start)
|
|
65
|
+
form.addRow("neuron_end", self.neuron_end)
|
|
66
|
+
form.addRow("bins", self.bins)
|
|
67
|
+
form.addRow("min_occupancy", self.min_occupancy)
|
|
68
|
+
form.addRow("smoothing", self.smoothing)
|
|
69
|
+
form.addRow("sigma", self.sigma)
|
|
70
|
+
form.addRow("autocorr overlap", self.overlap)
|
|
71
|
+
form.addRow("mode", self.mode)
|
|
72
|
+
form.addRow("score threshold (viz)", self.score_thr)
|
|
73
|
+
|
|
74
|
+
return box
|
|
75
|
+
|
|
76
|
+
def collect_params(self) -> dict:
|
|
77
|
+
return {
|
|
78
|
+
"gridscore": {
|
|
79
|
+
"neuron_start": int(self.neuron_start.value()),
|
|
80
|
+
"neuron_end": int(self.neuron_end.value()),
|
|
81
|
+
"bins": int(self.bins.value()),
|
|
82
|
+
"min_occupancy": int(self.min_occupancy.value()),
|
|
83
|
+
"smoothing": bool(self.smoothing.isChecked()),
|
|
84
|
+
"sigma": float(self.sigma.value()),
|
|
85
|
+
"overlap": float(self.overlap.value()),
|
|
86
|
+
"mode": str(self.mode.currentText()),
|
|
87
|
+
"score_thr": float(self.score_thr.value()),
|
|
88
|
+
"neuron_id": int(self.neuron_id.value()),
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
def apply_ranges(self, neuron_count: int | None, total_steps: int | None) -> None:
|
|
93
|
+
if neuron_count is None:
|
|
94
|
+
return
|
|
95
|
+
max_neuron = max(0, neuron_count - 1)
|
|
96
|
+
self.neuron_start.setRange(0, max_neuron)
|
|
97
|
+
self.neuron_end.setRange(0, neuron_count)
|
|
98
|
+
if self.neuron_end.value() == 0:
|
|
99
|
+
self.neuron_end.setValue(neuron_count)
|
|
100
|
+
self.neuron_id.setRange(0, max_neuron)
|
|
101
|
+
|
|
102
|
+
def apply_meta(self, meta: dict[str, Any]) -> None:
|
|
103
|
+
if not isinstance(meta, dict):
|
|
104
|
+
return
|
|
105
|
+
if "bins" in meta:
|
|
106
|
+
self.bins.setValue(int(meta["bins"]))
|
|
107
|
+
if "min_occupancy" in meta:
|
|
108
|
+
self.min_occupancy.setValue(int(meta["min_occupancy"]))
|
|
109
|
+
if "smoothing" in meta:
|
|
110
|
+
self.smoothing.setChecked(bool(meta["smoothing"]))
|
|
111
|
+
if "sigma" in meta:
|
|
112
|
+
self.sigma.setValue(float(meta["sigma"]))
|
|
113
|
+
if "overlap" in meta:
|
|
114
|
+
self.overlap.setValue(float(meta["overlap"]))
|
|
115
|
+
if "mode" in meta:
|
|
116
|
+
self.mode.setCurrentText(str(meta["mode"]))
|
|
117
|
+
if "neuron_id" in meta:
|
|
118
|
+
self.neuron_id.setValue(int(meta["neuron_id"]))
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class GridScoreInspectMode(GridScoreMode):
|
|
122
|
+
name = "gridscore_inspect"
|
|
123
|
+
display_name = "GridScore Inspect"
|