rcbench 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.
- rcbench-0.1.0/LICENSE +21 -0
- rcbench-0.1.0/PKG-INFO +241 -0
- rcbench-0.1.0/README.md +198 -0
- rcbench-0.1.0/rcbench/__init__.py +13 -0
- rcbench-0.1.0/rcbench/classes/Measurement.py +84 -0
- rcbench-0.1.0/rcbench/classes/__init__.py +0 -0
- rcbench-0.1.0/rcbench/classes/sample.py +85 -0
- rcbench-0.1.0/rcbench/logger.py +48 -0
- rcbench-0.1.0/rcbench/measurements/__init__.py +0 -0
- rcbench-0.1.0/rcbench/measurements/dataset.py +219 -0
- rcbench-0.1.0/rcbench/measurements/loader.py +72 -0
- rcbench-0.1.0/rcbench/measurements/parser.py +227 -0
- rcbench-0.1.0/rcbench/tasks/__init__.py +1 -0
- rcbench-0.1.0/rcbench/tasks/baseevaluator.py +112 -0
- rcbench-0.1.0/rcbench/tasks/featureselector.py +209 -0
- rcbench-0.1.0/rcbench/tasks/generalizationrank.py +97 -0
- rcbench-0.1.0/rcbench/tasks/kernelrank.py +97 -0
- rcbench-0.1.0/rcbench/tasks/memorycapacity.py +350 -0
- rcbench-0.1.0/rcbench/tasks/narma.py +278 -0
- rcbench-0.1.0/rcbench/tasks/nlt.py +308 -0
- rcbench-0.1.0/rcbench/tasks/sinx.py +160 -0
- rcbench-0.1.0/rcbench/utils/__init__.py +0 -0
- rcbench-0.1.0/rcbench/utils/utils.py +117 -0
- rcbench-0.1.0/rcbench/visualization/__init__.py +0 -0
- rcbench-0.1.0/rcbench/visualization/base_plotter.py +421 -0
- rcbench-0.1.0/rcbench/visualization/mc_plotter.py +227 -0
- rcbench-0.1.0/rcbench/visualization/narma_plotter.py +169 -0
- rcbench-0.1.0/rcbench/visualization/nlt_plotter.py +389 -0
- rcbench-0.1.0/rcbench/visualization/plot_config.py +321 -0
- rcbench-0.1.0/rcbench/visualization/sinx_plotter.py +169 -0
- rcbench-0.1.0/rcbench.egg-info/PKG-INFO +241 -0
- rcbench-0.1.0/rcbench.egg-info/SOURCES.txt +37 -0
- rcbench-0.1.0/rcbench.egg-info/dependency_links.txt +1 -0
- rcbench-0.1.0/rcbench.egg-info/requires.txt +14 -0
- rcbench-0.1.0/rcbench.egg-info/top_level.txt +1 -0
- rcbench-0.1.0/setup.cfg +4 -0
- rcbench-0.1.0/setup.py +47 -0
- rcbench-0.1.0/tests/test_electrode_selection.py +689 -0
- rcbench-0.1.0/tests/test_reservoir_dataset_consistency.py +281 -0
rcbench-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Davide Pilati
|
|
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.
|
rcbench-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rcbench
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Reservoir computing benchmark toolkit
|
|
5
|
+
Home-page: https://github.com/nanotechdave/RCbench
|
|
6
|
+
Author: Davide Pilati
|
|
7
|
+
Author-email: davide.pilati@polito.it
|
|
8
|
+
License: MIT
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Requires-Python: >=3.9
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
License-File: LICENSE
|
|
19
|
+
Requires-Dist: numpy
|
|
20
|
+
Requires-Dist: scipy
|
|
21
|
+
Requires-Dist: matplotlib
|
|
22
|
+
Requires-Dist: scikit-learn
|
|
23
|
+
Requires-Dist: pandas
|
|
24
|
+
Provides-Extra: test
|
|
25
|
+
Requires-Dist: pytest; extra == "test"
|
|
26
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: bump2version; extra == "dev"
|
|
29
|
+
Requires-Dist: twine; extra == "dev"
|
|
30
|
+
Requires-Dist: build; extra == "dev"
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: classifier
|
|
34
|
+
Dynamic: description
|
|
35
|
+
Dynamic: description-content-type
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: license
|
|
38
|
+
Dynamic: license-file
|
|
39
|
+
Dynamic: provides-extra
|
|
40
|
+
Dynamic: requires-dist
|
|
41
|
+
Dynamic: requires-python
|
|
42
|
+
Dynamic: summary
|
|
43
|
+
|
|
44
|
+
# RCbench - Reservoir Computing Benchmark Toolkit
|
|
45
|
+
|
|
46
|
+

|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
**RCbench (Reservoir Computing Benchmark Toolkit)** is a comprehensive Python package for evaluating and benchmarking reservoir computing systems. It provides standardized tasks, flexible visualization tools, and efficient evaluation methods for both physical and simulated reservoirs.
|
|
51
|
+
|
|
52
|
+
## 🚀 Features
|
|
53
|
+
|
|
54
|
+
RCbench provides:
|
|
55
|
+
|
|
56
|
+
-**Multiple Benchmark Tasks:**
|
|
57
|
+
-**NLT (Nonlinear Transformation):** Evaluate reservoir performance on standard nonlinear transformations
|
|
58
|
+
-**NARMA:** Test with Nonlinear Auto-Regressive Moving Average models of different orders
|
|
59
|
+
-**Memory Capacity:** Measure the short and long-term memory capabilities
|
|
60
|
+
-**Sin(x):** Assess reservoir ability to transform a random signal into a nonlinear function that use the input as argument
|
|
61
|
+
-**KernelRank:** Evaluates the nonlinearityof the reservoir
|
|
62
|
+
-**GeneralizationRank:** Evaluates the generalization capabilities of the reservoir
|
|
63
|
+
|
|
64
|
+
-**Advanced Visualization:**
|
|
65
|
+
-Task-specific plotters with customizable configurations
|
|
66
|
+
-General reservoir properties visualization (input signals, output responses, nonlinearity)
|
|
67
|
+
-Frequency domain analysis of reservoir behavior
|
|
68
|
+
-Target vs. prediction comparison with proper time alignment
|
|
69
|
+
|
|
70
|
+
-**Efficient Data Handling:**
|
|
71
|
+
-Automatic measurement loading and parsing
|
|
72
|
+
-Support for various experimental data formats
|
|
73
|
+
-Feature selection and dimensionality reduction tools
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 📂 Project Structure
|
|
77
|
+
|
|
78
|
+
```plaintext
|
|
79
|
+
RCbench/
|
|
80
|
+
├── rcbench/
|
|
81
|
+
| |── examples/ # Example scripts
|
|
82
|
+
| | ├── example_nlt.py
|
|
83
|
+
| | ├── example_NARMA.py
|
|
84
|
+
| | ├── example_sinx.py
|
|
85
|
+
| | └── example_MC.py
|
|
86
|
+
│ ├── measurements/ # Data handling
|
|
87
|
+
│ │ ├── dataset.py # ReservoirDataset class
|
|
88
|
+
│ │ ├── loader.py # Data loading utilities
|
|
89
|
+
│ │ └── parser.py # Data parsing utilities
|
|
90
|
+
│ ├── tasks/ # Reservoir computing tasks
|
|
91
|
+
│ │ ├── baseevaluator.py # Base evaluation methods
|
|
92
|
+
│ │ ├── nlt.py # Nonlinear Transformation
|
|
93
|
+
│ │ ├── narma.py # NARMA tasks
|
|
94
|
+
│ │ ├── memorycapacity.py # Memory Capacity
|
|
95
|
+
│ │ ├── sinx.py # Sin(x) approximation
|
|
96
|
+
│ │ └── featureselector.py # Feature selection tools
|
|
97
|
+
│ ├── visualization/ # Plotting utilities
|
|
98
|
+
│ │ ├── base_plotter.py # Base plotting functionality
|
|
99
|
+
│ │ ├── plot_config.py # Plot configurations
|
|
100
|
+
│ │ ├── nlt_plotter.py # NLT visualization
|
|
101
|
+
│ │ ├── narma_plotter.py # NARMA visualization
|
|
102
|
+
│ │ └── sinx_plotter.py # Sin(x) visualization
|
|
103
|
+
│ └── logger.py # Logging utilities
|
|
104
|
+
└
|
|
105
|
+
```
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 🔧 Installation
|
|
109
|
+
|
|
110
|
+
**Install directly from GitHub:**
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
pip install rcbench
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
Or, install locally (development mode):
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git clone https://github.com/nanotechdave/RCbench.git
|
|
121
|
+
cd RCbench
|
|
122
|
+
pip install -e .
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
## 🚦 Usage Example
|
|
127
|
+
Here's a quick example demonstrating how to perform an NLT evaluation:
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
import logging
|
|
131
|
+
from pathlib import Path
|
|
132
|
+
import numpy as np
|
|
133
|
+
import matplotlib.pyplot as plt
|
|
134
|
+
|
|
135
|
+
from rcbench import ReservoirDataset
|
|
136
|
+
from rcbench import NltEvaluator
|
|
137
|
+
from rcbench.visualization.plot_config import NLTPlotConfig
|
|
138
|
+
from rcbench.logger import get_logger
|
|
139
|
+
|
|
140
|
+
logger = get_logger(__name__)
|
|
141
|
+
logger.setLevel(logging.INFO)
|
|
142
|
+
|
|
143
|
+
BASE_DIR = "FILE_PATH"
|
|
144
|
+
|
|
145
|
+
filenameNLT = "YOUR_FILENAME"
|
|
146
|
+
|
|
147
|
+
measurement_file_NLT = BASE_DIR / filenameNLT
|
|
148
|
+
|
|
149
|
+
# Load the data directly using the ReservoirDataset class
|
|
150
|
+
dataset = ReservoirDataset(measurement_file_NLT)
|
|
151
|
+
|
|
152
|
+
# Get information about the electrodes
|
|
153
|
+
electrodes_info = dataset.summary()
|
|
154
|
+
logger.info(f"Parsed Electrodes: {electrodes_info}")
|
|
155
|
+
|
|
156
|
+
# Get input and node voltages directly from the dataset
|
|
157
|
+
input_elec = electrodes_info['input_electrodes'][0]
|
|
158
|
+
input_signal = dataset.get_input_voltages()[input_elec]
|
|
159
|
+
time = dataset.time
|
|
160
|
+
|
|
161
|
+
# Get node voltages (only node electrodes, not input)
|
|
162
|
+
nodes_output = dataset.get_node_voltages()
|
|
163
|
+
electrode_names = electrodes_info['node_electrodes']
|
|
164
|
+
|
|
165
|
+
# Create NLT plot configuration
|
|
166
|
+
plot_config = NLTPlotConfig(
|
|
167
|
+
save_dir=None, # Save plots to this directory
|
|
168
|
+
|
|
169
|
+
# General reservoir property plots
|
|
170
|
+
plot_input_signal=True, # Plot the input signal
|
|
171
|
+
plot_output_responses=True, # Plot node responses
|
|
172
|
+
plot_nonlinearity=True, # Plot nonlinearity of nodes
|
|
173
|
+
plot_frequency_analysis=True, # Plot frequency analysis
|
|
174
|
+
|
|
175
|
+
# Target-specific plots
|
|
176
|
+
plot_target_prediction=True, # Plot target vs prediction results
|
|
177
|
+
|
|
178
|
+
# Plot styling options
|
|
179
|
+
nonlinearity_plot_style='scatter',
|
|
180
|
+
frequency_range=(0, 20) # Limit frequency range to 0-20 Hz for clearer visualization
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Run NLT evaluation with plot config
|
|
184
|
+
evaluatorNLT = NltEvaluator(
|
|
185
|
+
input_signal=input_signal,
|
|
186
|
+
nodes_output=nodes_output,
|
|
187
|
+
time_array=time,
|
|
188
|
+
waveform_type='sine',
|
|
189
|
+
electrode_names=electrode_names,
|
|
190
|
+
plot_config=plot_config
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
resultsNLT = {}
|
|
195
|
+
for target_name in evaluatorNLT.targets:
|
|
196
|
+
try:
|
|
197
|
+
result = evaluatorNLT.run_evaluation(
|
|
198
|
+
target_name=target_name,
|
|
199
|
+
metric='NMSE',
|
|
200
|
+
feature_selection_method='pca',
|
|
201
|
+
num_features='all',
|
|
202
|
+
model="Ridge",
|
|
203
|
+
regression_alpha=0.01,
|
|
204
|
+
train_ratio=0.8,
|
|
205
|
+
plot=False, # Don't plot during evaluation
|
|
206
|
+
)
|
|
207
|
+
resultsNLT[target_name] = result
|
|
208
|
+
# Print results clearly
|
|
209
|
+
logger.output(f"NLT Analysis for Target: '{target_name}'")
|
|
210
|
+
logger.output(f" - Metric: {result['metric']}")
|
|
211
|
+
logger.output(f" - Accuracy: {result['accuracy']:.5f}")
|
|
212
|
+
logger.output(f" - Selected Features Indices: {[electrode_names[i] for i in result['selected_features']]}")
|
|
213
|
+
logger.output(f" - Model Weights: {result['model'].coef_}\n")
|
|
214
|
+
except Exception as e:
|
|
215
|
+
logger.error(f"Error evaluating {target_name}: {str(e)}")
|
|
216
|
+
|
|
217
|
+
evaluatorNLT.plot_results(existing_results=resultsNLT)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## 📈 Visualization Tools
|
|
221
|
+
RCbench features a unified visualization system with:
|
|
222
|
+
-**Task-Specific Plotters:** Dedicated plotters for each task (NLTPlotter, NarmaPlotter, SinxPlotter)
|
|
223
|
+
-**Customizable Configurations:** Control which plots to generate through configuration objects
|
|
224
|
+
-**Comprehensive Visualization:** For each task, view:
|
|
225
|
+
-General reservoir properties (input signals, node responses, nonlinearity)
|
|
226
|
+
-Frequency domain analysis
|
|
227
|
+
-Target vs. prediction comparisons
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
## 📝 Contributions & Issues
|
|
232
|
+
Contributions are welcome! Please open a pull request or an issue on GitHub.
|
|
233
|
+
|
|
234
|
+
- Issue Tracker: https://github.com/nanotechdave/RCbench/issues
|
|
235
|
+
|
|
236
|
+
- Pull Requests: https://github.com/nanotechdave/RCbench/pulls
|
|
237
|
+
|
|
238
|
+
## 📜 License
|
|
239
|
+
|
|
240
|
+
RCbench is licensed under the MIT License. See the LICENSE file for details.
|
|
241
|
+
|
rcbench-0.1.0/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# RCbench - Reservoir Computing Benchmark Toolkit
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
**RCbench (Reservoir Computing Benchmark Toolkit)** is a comprehensive Python package for evaluating and benchmarking reservoir computing systems. It provides standardized tasks, flexible visualization tools, and efficient evaluation methods for both physical and simulated reservoirs.
|
|
8
|
+
|
|
9
|
+
## 🚀 Features
|
|
10
|
+
|
|
11
|
+
RCbench provides:
|
|
12
|
+
|
|
13
|
+
-**Multiple Benchmark Tasks:**
|
|
14
|
+
-**NLT (Nonlinear Transformation):** Evaluate reservoir performance on standard nonlinear transformations
|
|
15
|
+
-**NARMA:** Test with Nonlinear Auto-Regressive Moving Average models of different orders
|
|
16
|
+
-**Memory Capacity:** Measure the short and long-term memory capabilities
|
|
17
|
+
-**Sin(x):** Assess reservoir ability to transform a random signal into a nonlinear function that use the input as argument
|
|
18
|
+
-**KernelRank:** Evaluates the nonlinearityof the reservoir
|
|
19
|
+
-**GeneralizationRank:** Evaluates the generalization capabilities of the reservoir
|
|
20
|
+
|
|
21
|
+
-**Advanced Visualization:**
|
|
22
|
+
-Task-specific plotters with customizable configurations
|
|
23
|
+
-General reservoir properties visualization (input signals, output responses, nonlinearity)
|
|
24
|
+
-Frequency domain analysis of reservoir behavior
|
|
25
|
+
-Target vs. prediction comparison with proper time alignment
|
|
26
|
+
|
|
27
|
+
-**Efficient Data Handling:**
|
|
28
|
+
-Automatic measurement loading and parsing
|
|
29
|
+
-Support for various experimental data formats
|
|
30
|
+
-Feature selection and dimensionality reduction tools
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## 📂 Project Structure
|
|
34
|
+
|
|
35
|
+
```plaintext
|
|
36
|
+
RCbench/
|
|
37
|
+
├── rcbench/
|
|
38
|
+
| |── examples/ # Example scripts
|
|
39
|
+
| | ├── example_nlt.py
|
|
40
|
+
| | ├── example_NARMA.py
|
|
41
|
+
| | ├── example_sinx.py
|
|
42
|
+
| | └── example_MC.py
|
|
43
|
+
│ ├── measurements/ # Data handling
|
|
44
|
+
│ │ ├── dataset.py # ReservoirDataset class
|
|
45
|
+
│ │ ├── loader.py # Data loading utilities
|
|
46
|
+
│ │ └── parser.py # Data parsing utilities
|
|
47
|
+
│ ├── tasks/ # Reservoir computing tasks
|
|
48
|
+
│ │ ├── baseevaluator.py # Base evaluation methods
|
|
49
|
+
│ │ ├── nlt.py # Nonlinear Transformation
|
|
50
|
+
│ │ ├── narma.py # NARMA tasks
|
|
51
|
+
│ │ ├── memorycapacity.py # Memory Capacity
|
|
52
|
+
│ │ ├── sinx.py # Sin(x) approximation
|
|
53
|
+
│ │ └── featureselector.py # Feature selection tools
|
|
54
|
+
│ ├── visualization/ # Plotting utilities
|
|
55
|
+
│ │ ├── base_plotter.py # Base plotting functionality
|
|
56
|
+
│ │ ├── plot_config.py # Plot configurations
|
|
57
|
+
│ │ ├── nlt_plotter.py # NLT visualization
|
|
58
|
+
│ │ ├── narma_plotter.py # NARMA visualization
|
|
59
|
+
│ │ └── sinx_plotter.py # Sin(x) visualization
|
|
60
|
+
│ └── logger.py # Logging utilities
|
|
61
|
+
└
|
|
62
|
+
```
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 🔧 Installation
|
|
66
|
+
|
|
67
|
+
**Install directly from GitHub:**
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pip install rcbench
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
Or, install locally (development mode):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git clone https://github.com/nanotechdave/RCbench.git
|
|
78
|
+
cd RCbench
|
|
79
|
+
pip install -e .
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
## 🚦 Usage Example
|
|
84
|
+
Here's a quick example demonstrating how to perform an NLT evaluation:
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
import logging
|
|
88
|
+
from pathlib import Path
|
|
89
|
+
import numpy as np
|
|
90
|
+
import matplotlib.pyplot as plt
|
|
91
|
+
|
|
92
|
+
from rcbench import ReservoirDataset
|
|
93
|
+
from rcbench import NltEvaluator
|
|
94
|
+
from rcbench.visualization.plot_config import NLTPlotConfig
|
|
95
|
+
from rcbench.logger import get_logger
|
|
96
|
+
|
|
97
|
+
logger = get_logger(__name__)
|
|
98
|
+
logger.setLevel(logging.INFO)
|
|
99
|
+
|
|
100
|
+
BASE_DIR = "FILE_PATH"
|
|
101
|
+
|
|
102
|
+
filenameNLT = "YOUR_FILENAME"
|
|
103
|
+
|
|
104
|
+
measurement_file_NLT = BASE_DIR / filenameNLT
|
|
105
|
+
|
|
106
|
+
# Load the data directly using the ReservoirDataset class
|
|
107
|
+
dataset = ReservoirDataset(measurement_file_NLT)
|
|
108
|
+
|
|
109
|
+
# Get information about the electrodes
|
|
110
|
+
electrodes_info = dataset.summary()
|
|
111
|
+
logger.info(f"Parsed Electrodes: {electrodes_info}")
|
|
112
|
+
|
|
113
|
+
# Get input and node voltages directly from the dataset
|
|
114
|
+
input_elec = electrodes_info['input_electrodes'][0]
|
|
115
|
+
input_signal = dataset.get_input_voltages()[input_elec]
|
|
116
|
+
time = dataset.time
|
|
117
|
+
|
|
118
|
+
# Get node voltages (only node electrodes, not input)
|
|
119
|
+
nodes_output = dataset.get_node_voltages()
|
|
120
|
+
electrode_names = electrodes_info['node_electrodes']
|
|
121
|
+
|
|
122
|
+
# Create NLT plot configuration
|
|
123
|
+
plot_config = NLTPlotConfig(
|
|
124
|
+
save_dir=None, # Save plots to this directory
|
|
125
|
+
|
|
126
|
+
# General reservoir property plots
|
|
127
|
+
plot_input_signal=True, # Plot the input signal
|
|
128
|
+
plot_output_responses=True, # Plot node responses
|
|
129
|
+
plot_nonlinearity=True, # Plot nonlinearity of nodes
|
|
130
|
+
plot_frequency_analysis=True, # Plot frequency analysis
|
|
131
|
+
|
|
132
|
+
# Target-specific plots
|
|
133
|
+
plot_target_prediction=True, # Plot target vs prediction results
|
|
134
|
+
|
|
135
|
+
# Plot styling options
|
|
136
|
+
nonlinearity_plot_style='scatter',
|
|
137
|
+
frequency_range=(0, 20) # Limit frequency range to 0-20 Hz for clearer visualization
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Run NLT evaluation with plot config
|
|
141
|
+
evaluatorNLT = NltEvaluator(
|
|
142
|
+
input_signal=input_signal,
|
|
143
|
+
nodes_output=nodes_output,
|
|
144
|
+
time_array=time,
|
|
145
|
+
waveform_type='sine',
|
|
146
|
+
electrode_names=electrode_names,
|
|
147
|
+
plot_config=plot_config
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
resultsNLT = {}
|
|
152
|
+
for target_name in evaluatorNLT.targets:
|
|
153
|
+
try:
|
|
154
|
+
result = evaluatorNLT.run_evaluation(
|
|
155
|
+
target_name=target_name,
|
|
156
|
+
metric='NMSE',
|
|
157
|
+
feature_selection_method='pca',
|
|
158
|
+
num_features='all',
|
|
159
|
+
model="Ridge",
|
|
160
|
+
regression_alpha=0.01,
|
|
161
|
+
train_ratio=0.8,
|
|
162
|
+
plot=False, # Don't plot during evaluation
|
|
163
|
+
)
|
|
164
|
+
resultsNLT[target_name] = result
|
|
165
|
+
# Print results clearly
|
|
166
|
+
logger.output(f"NLT Analysis for Target: '{target_name}'")
|
|
167
|
+
logger.output(f" - Metric: {result['metric']}")
|
|
168
|
+
logger.output(f" - Accuracy: {result['accuracy']:.5f}")
|
|
169
|
+
logger.output(f" - Selected Features Indices: {[electrode_names[i] for i in result['selected_features']]}")
|
|
170
|
+
logger.output(f" - Model Weights: {result['model'].coef_}\n")
|
|
171
|
+
except Exception as e:
|
|
172
|
+
logger.error(f"Error evaluating {target_name}: {str(e)}")
|
|
173
|
+
|
|
174
|
+
evaluatorNLT.plot_results(existing_results=resultsNLT)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 📈 Visualization Tools
|
|
178
|
+
RCbench features a unified visualization system with:
|
|
179
|
+
-**Task-Specific Plotters:** Dedicated plotters for each task (NLTPlotter, NarmaPlotter, SinxPlotter)
|
|
180
|
+
-**Customizable Configurations:** Control which plots to generate through configuration objects
|
|
181
|
+
-**Comprehensive Visualization:** For each task, view:
|
|
182
|
+
-General reservoir properties (input signals, node responses, nonlinearity)
|
|
183
|
+
-Frequency domain analysis
|
|
184
|
+
-Target vs. prediction comparisons
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
## 📝 Contributions & Issues
|
|
189
|
+
Contributions are welcome! Please open a pull request or an issue on GitHub.
|
|
190
|
+
|
|
191
|
+
- Issue Tracker: https://github.com/nanotechdave/RCbench/issues
|
|
192
|
+
|
|
193
|
+
- Pull Requests: https://github.com/nanotechdave/RCbench/pulls
|
|
194
|
+
|
|
195
|
+
## 📜 License
|
|
196
|
+
|
|
197
|
+
RCbench is licensed under the MIT License. See the LICENSE file for details.
|
|
198
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
__version__ = "0.0.10.dev1"
|
|
2
|
+
|
|
3
|
+
from .utils import utils
|
|
4
|
+
from .tasks.nlt import NltEvaluator
|
|
5
|
+
from .tasks.memorycapacity import MemoryCapacityEvaluator
|
|
6
|
+
from .tasks.sinx import SinxEvaluator
|
|
7
|
+
from .tasks.kernelrank import KernelRankEvaluator
|
|
8
|
+
from .tasks.generalizationrank import GeneralizationRankEvaluator
|
|
9
|
+
from .tasks.narma import NarmaEvaluator
|
|
10
|
+
from .measurements.parser import MeasurementParser
|
|
11
|
+
from .measurements.loader import MeasurementLoader
|
|
12
|
+
from .measurements.dataset import ReservoirDataset
|
|
13
|
+
from .logger import get_logger
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from rcbench.measurements.dataset import ReservoirDataset
|
|
4
|
+
from rcbench.measurements.parser import MeasurementParser
|
|
5
|
+
from rcbench.logger import get_logger
|
|
6
|
+
|
|
7
|
+
logger = get_logger(__name__)
|
|
8
|
+
|
|
9
|
+
class MeasurementType(Enum):
|
|
10
|
+
"""Enumeration of possible measurement types."""
|
|
11
|
+
NLT = "nlt"
|
|
12
|
+
MEMORY_CAPACITY = "mc"
|
|
13
|
+
KERNEL_RANK = "kernel"
|
|
14
|
+
ACTIVATION = "activation"
|
|
15
|
+
UNKNOWN = "unknown"
|
|
16
|
+
|
|
17
|
+
class Measurement():
|
|
18
|
+
"""Base class for all measurement types."""
|
|
19
|
+
def __init__(self, path: str):
|
|
20
|
+
"""
|
|
21
|
+
Initialize a Measurement object.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
path (str): Path to the measurement file
|
|
25
|
+
"""
|
|
26
|
+
self.path = path
|
|
27
|
+
self.type = self._determine_measurement_type()
|
|
28
|
+
self.dataset: Optional[ReservoirDataset] = None
|
|
29
|
+
self.parser: Optional[MeasurementParser] = None
|
|
30
|
+
self._load_measurement()
|
|
31
|
+
|
|
32
|
+
def _determine_measurement_type(self) -> MeasurementType:
|
|
33
|
+
"""Determine the type of measurement based on the filename."""
|
|
34
|
+
filename = self.path.lower()
|
|
35
|
+
if "nlt" in filename or "nonlinear" in filename:
|
|
36
|
+
return MeasurementType.NLT
|
|
37
|
+
elif "mc" in filename or "memory" in filename:
|
|
38
|
+
return MeasurementType.MEMORY_CAPACITY
|
|
39
|
+
elif "kernel" in filename:
|
|
40
|
+
return MeasurementType.KERNEL_RANK
|
|
41
|
+
elif "activation" in filename:
|
|
42
|
+
return MeasurementType.ACTIVATION
|
|
43
|
+
return MeasurementType.UNKNOWN
|
|
44
|
+
|
|
45
|
+
def _load_measurement(self):
|
|
46
|
+
"""Load the measurement data using MeasurementLoader and MeasurementParser."""
|
|
47
|
+
try:
|
|
48
|
+
from rcbench.measurements.loader import MeasurementLoader
|
|
49
|
+
loader = MeasurementLoader(self.path)
|
|
50
|
+
self.dataset = loader.get_dataset()
|
|
51
|
+
self.parser = MeasurementParser(self.dataset)
|
|
52
|
+
logger.info(f"Successfully loaded measurement of type {self.type.value}")
|
|
53
|
+
except Exception as e:
|
|
54
|
+
logger.error(f"Error loading measurement {self.path}: {str(e)}")
|
|
55
|
+
raise
|
|
56
|
+
|
|
57
|
+
def get_input_voltages(self):
|
|
58
|
+
"""Get input voltages from the measurement."""
|
|
59
|
+
if self.parser:
|
|
60
|
+
return self.parser.get_input_voltages()
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
def get_node_voltages(self):
|
|
64
|
+
"""Get node voltages from the measurement."""
|
|
65
|
+
if self.parser:
|
|
66
|
+
return self.parser.get_node_voltages()
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
def get_time(self):
|
|
70
|
+
"""Get time data from the measurement."""
|
|
71
|
+
if self.dataset:
|
|
72
|
+
return self.dataset.time
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
def __str__(self):
|
|
76
|
+
return f"Measurement(type={self.type.value}, path={self.path})"
|
|
77
|
+
|
|
78
|
+
class MemoryCapacity(Measurement):
|
|
79
|
+
"""Memory Capacity specific measurement class."""
|
|
80
|
+
def __init__(self, path: str):
|
|
81
|
+
super().__init__(path)
|
|
82
|
+
if self.type != MeasurementType.MEMORY_CAPACITY:
|
|
83
|
+
logger.warning(f"File {path} may not be a memory capacity measurement")
|
|
84
|
+
|
|
File without changes
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import List, Dict
|
|
3
|
+
from rcbench.classes.Measurement import Measurement, MeasurementType
|
|
4
|
+
from rcbench.logger import get_logger
|
|
5
|
+
|
|
6
|
+
logger = get_logger(__name__)
|
|
7
|
+
|
|
8
|
+
class Sample():
|
|
9
|
+
""" Sample data class. Instance is an object containing all the measurements of a sample. """
|
|
10
|
+
def __init__(self, name: str, path: str):
|
|
11
|
+
"""
|
|
12
|
+
Initialize a Sample object.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
name (str): Name of the sample
|
|
16
|
+
path (str): Path to the folder containing measurement files
|
|
17
|
+
"""
|
|
18
|
+
self.name = name
|
|
19
|
+
self.path = Path(path)
|
|
20
|
+
self.measurements: Dict[str, Measurement] = {}
|
|
21
|
+
self._scan_measurements()
|
|
22
|
+
|
|
23
|
+
def _scan_measurements(self):
|
|
24
|
+
"""Scan the folder for measurement files and initialize measurement objects."""
|
|
25
|
+
if not self.path.exists():
|
|
26
|
+
raise FileNotFoundError(f"Folder not found: {self.path}")
|
|
27
|
+
|
|
28
|
+
# Find all .txt files that don't contain 'log' in their name
|
|
29
|
+
measurement_files = [f for f in self.path.glob("*.txt") if "log" not in f.name.lower()]
|
|
30
|
+
|
|
31
|
+
for file_path in measurement_files:
|
|
32
|
+
try:
|
|
33
|
+
# Create appropriate measurement object based on file type
|
|
34
|
+
measurement = Measurement(str(file_path))
|
|
35
|
+
|
|
36
|
+
# Store the measurement object with the filename as key
|
|
37
|
+
self.measurements[file_path.name] = measurement
|
|
38
|
+
logger.info(f"Loaded measurement: {file_path.name} (type: {measurement.type.value})")
|
|
39
|
+
|
|
40
|
+
except Exception as e:
|
|
41
|
+
logger.error(f"Error loading measurement {file_path.name}: {str(e)}")
|
|
42
|
+
|
|
43
|
+
def get_measurement(self, filename: str) -> Measurement:
|
|
44
|
+
"""
|
|
45
|
+
Get a specific measurement by filename.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
filename (str): Name of the measurement file
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Measurement: The measurement object
|
|
52
|
+
"""
|
|
53
|
+
if filename not in self.measurements:
|
|
54
|
+
raise KeyError(f"Measurement {filename} not found")
|
|
55
|
+
return self.measurements[filename]
|
|
56
|
+
|
|
57
|
+
def get_measurements_by_type(self, measurement_type: MeasurementType) -> List[Measurement]:
|
|
58
|
+
"""
|
|
59
|
+
Get all measurements of a specific type.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
measurement_type (MeasurementType): Type of measurements to retrieve
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List[Measurement]: List of measurements of the specified type
|
|
66
|
+
"""
|
|
67
|
+
return [m for m in self.measurements.values() if m.type == measurement_type]
|
|
68
|
+
|
|
69
|
+
def list_measurements(self) -> List[str]:
|
|
70
|
+
"""
|
|
71
|
+
Get a list of all available measurement filenames.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
List[str]: List of measurement filenames
|
|
75
|
+
"""
|
|
76
|
+
return list(self.measurements.keys())
|
|
77
|
+
|
|
78
|
+
def __str__(self):
|
|
79
|
+
measurement_types = {}
|
|
80
|
+
for m in self.measurements.values():
|
|
81
|
+
measurement_types[m.type.value] = measurement_types.get(m.type.value, 0) + 1
|
|
82
|
+
|
|
83
|
+
type_str = ", ".join(f"{t}: {c}" for t, c in measurement_types.items())
|
|
84
|
+
objstr = f"Sample NWN_Pad{self.name} with {len(self.measurements)} measurements ({type_str})"
|
|
85
|
+
return objstr
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import sys
|
|
3
|
+
|
|
4
|
+
OUTPUT_LEVEL = 25
|
|
5
|
+
logging.addLevelName(OUTPUT_LEVEL, "OUTPUT")
|
|
6
|
+
|
|
7
|
+
# Define color ANSI codes
|
|
8
|
+
COLOR_RESET = "\033[0m"
|
|
9
|
+
COLORS = {
|
|
10
|
+
logging.DEBUG: "\033[36m", # Cyan
|
|
11
|
+
logging.INFO: "\033[34m", # Blue
|
|
12
|
+
OUTPUT_LEVEL: "\033[32m", # Green
|
|
13
|
+
logging.WARNING: "\033[33m", # Yellow
|
|
14
|
+
logging.ERROR: "\033[31m", # Red
|
|
15
|
+
logging.CRITICAL: "\033[41m", # Red Background
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
def output(self, message, *args, **kwargs):
|
|
19
|
+
if self.isEnabledFor(OUTPUT_LEVEL):
|
|
20
|
+
self._log(OUTPUT_LEVEL, message, args, **kwargs)
|
|
21
|
+
|
|
22
|
+
logging.Logger.output = output
|
|
23
|
+
|
|
24
|
+
class ColoredFormatter(logging.Formatter):
|
|
25
|
+
def format(self, record):
|
|
26
|
+
level_color = COLORS.get(record.levelno, COLOR_RESET)
|
|
27
|
+
levelname = f"{level_color}[{record.levelname}]{COLOR_RESET}"
|
|
28
|
+
|
|
29
|
+
formatter = logging.Formatter(
|
|
30
|
+
f"%(asctime)s {levelname} %(name)s — %(message)s",
|
|
31
|
+
datefmt="%H:%M:%S"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return formatter.format(record)
|
|
35
|
+
|
|
36
|
+
def get_logger(name: str = "rcda"):
|
|
37
|
+
logger = logging.getLogger(name)
|
|
38
|
+
|
|
39
|
+
if not logger.handlers:
|
|
40
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
41
|
+
handler.setFormatter(ColoredFormatter())
|
|
42
|
+
logger.addHandler(handler)
|
|
43
|
+
|
|
44
|
+
logger.propagate = False
|
|
45
|
+
# Keep level unset here to control externally
|
|
46
|
+
# logger.setLevel(logging.INFO)
|
|
47
|
+
|
|
48
|
+
return logger
|