ber-equalization-studio 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 (24) hide show
  1. ber_equalization_studio-0.1.0/PKG-INFO +266 -0
  2. ber_equalization_studio-0.1.0/README.md +231 -0
  3. ber_equalization_studio-0.1.0/pyproject.toml +53 -0
  4. ber_equalization_studio-0.1.0/setup.cfg +4 -0
  5. ber_equalization_studio-0.1.0/src/ber_equalization_studio/__init__.py +58 -0
  6. ber_equalization_studio-0.1.0/src/ber_equalization_studio/_legacy_backend/__init__.py +1 -0
  7. ber_equalization_studio-0.1.0/src/ber_equalization_studio/_legacy_backend/ber_equalization.py +3700 -0
  8. ber_equalization_studio-0.1.0/src/ber_equalization_studio/_legacy_backend/efficient_kan/__init__.py +3 -0
  9. ber_equalization_studio-0.1.0/src/ber_equalization_studio/_legacy_backend/efficient_kan/kan.py +218 -0
  10. ber_equalization_studio-0.1.0/src/ber_equalization_studio/api.py +348 -0
  11. ber_equalization_studio-0.1.0/src/ber_equalization_studio/cli.py +92 -0
  12. ber_equalization_studio-0.1.0/src/ber_equalization_studio/config.py +168 -0
  13. ber_equalization_studio-0.1.0/src/ber_equalization_studio/data.py +31 -0
  14. ber_equalization_studio-0.1.0/src/ber_equalization_studio/experiment.py +92 -0
  15. ber_equalization_studio-0.1.0/src/ber_equalization_studio/legacy.py +149 -0
  16. ber_equalization_studio-0.1.0/src/ber_equalization_studio/models.py +86 -0
  17. ber_equalization_studio-0.1.0/src/ber_equalization_studio/results.py +74 -0
  18. ber_equalization_studio-0.1.0/src/ber_equalization_studio/visualization.py +186 -0
  19. ber_equalization_studio-0.1.0/src/ber_equalization_studio.egg-info/PKG-INFO +266 -0
  20. ber_equalization_studio-0.1.0/src/ber_equalization_studio.egg-info/SOURCES.txt +22 -0
  21. ber_equalization_studio-0.1.0/src/ber_equalization_studio.egg-info/dependency_links.txt +1 -0
  22. ber_equalization_studio-0.1.0/src/ber_equalization_studio.egg-info/entry_points.txt +2 -0
  23. ber_equalization_studio-0.1.0/src/ber_equalization_studio.egg-info/requires.txt +23 -0
  24. ber_equalization_studio-0.1.0/src/ber_equalization_studio.egg-info/top_level.txt +1 -0
@@ -0,0 +1,266 @@
1
+ Metadata-Version: 2.4
2
+ Name: ber-equalization-studio
3
+ Version: 0.1.0
4
+ Summary: Research studio for BER equalization experiments in nonlinear optical links.
5
+ Keywords: ber,equalization,optical-communications,photonics,pytorch
6
+ Classifier: Development Status :: 3 - Alpha
7
+ Classifier: Intended Audience :: Science/Research
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
13
+ Classifier: Topic :: Scientific/Engineering :: Physics
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: matplotlib>=3.8
17
+ Requires-Dist: numpy>=1.24
18
+ Requires-Dist: pandas>=2.0
19
+ Requires-Dist: plotly>=5.18
20
+ Requires-Dist: torch>=2.1
21
+ Provides-Extra: kan
22
+ Provides-Extra: notebook
23
+ Requires-Dist: ipykernel>=6.29; extra == "notebook"
24
+ Requires-Dist: ipython>=8.18; extra == "notebook"
25
+ Requires-Dist: ipywidgets>=8.1; extra == "notebook"
26
+ Requires-Dist: jupyterlab>=4.0; extra == "notebook"
27
+ Requires-Dist: nbformat>=5.9; extra == "notebook"
28
+ Provides-Extra: dev
29
+ Requires-Dist: build>=1.2; extra == "dev"
30
+ Requires-Dist: pytest>=8.0; extra == "dev"
31
+ Requires-Dist: twine>=5.1; extra == "dev"
32
+ Provides-Extra: publish
33
+ Requires-Dist: build>=1.2; extra == "publish"
34
+ Requires-Dist: twine>=5.1; extra == "publish"
35
+
36
+ # BER Equalization Studio
37
+
38
+ Research library for BER equalization experiments in nonlinear fiber-optic links.
39
+
40
+ The studio wraps the existing, validated `BER_minimization_survey/ber_equalization.py`
41
+ backend with a cleaner API:
42
+
43
+ - structured dataclass configs instead of editing one large global `Config`;
44
+ - model registry and aliases;
45
+ - reusable experiment runner;
46
+ - per-model run folders with checkpoints, metrics, histories, and plots;
47
+ - result comparison helpers for paper tables;
48
+ - Plotly HTML visualizations for BER/loss curves and model tradeoffs.
49
+
50
+ ## Layout
51
+
52
+ ```text
53
+ ber-equalization-studio/
54
+ src/ber_equalization_studio/
55
+ config.py # DataConfig, ModelConfig, TrainingConfig, StudioConfig
56
+ legacy.py # adapter to the original ber_equalization.py backend
57
+ models.py # model registry and constructors
58
+ data.py # dataset preparation wrapper
59
+ experiment.py # ExperimentRunner
60
+ api.py # notebook-friendly Studio, RunResult, StudyResult
61
+ results.py # result loading/comparison helpers
62
+ visualization.py # BER/loss/complexity plots
63
+ cli.py # ber-studio command
64
+ examples/
65
+ run_smoke.py
66
+ ```
67
+
68
+ ## Quick Start
69
+
70
+ Install from PyPI:
71
+
72
+ ```powershell
73
+ python -m pip install "ber-equalization-studio[notebook]"
74
+ ```
75
+
76
+ For local development from the repository root:
77
+
78
+ ```powershell
79
+ cd ber-equalization-studio
80
+ python -m pip install -e ".[notebook,dev]"
81
+ ber-studio models
82
+ ```
83
+
84
+ Run a short smoke experiment:
85
+
86
+ ```powershell
87
+ ber-studio run `
88
+ --name smoke_complex_fastkan `
89
+ --models complex_fastkan,mlp `
90
+ --data-dir ..\BER_minimization_survey\symbols_new `
91
+ --epochs 5 `
92
+ --max-test-files 1
93
+ ```
94
+
95
+ If your symbol CSV files live directly under `BER_minimization_survey`, pass that
96
+ folder instead. The backend searches for files named:
97
+
98
+ ```text
99
+ Symbols_1m_1ch_PR_*.csv
100
+ ```
101
+
102
+ The PyPI package includes the legacy training backend and the local EfficientKAN
103
+ implementation used by the studio. You only need to pass data directories with symbol
104
+ CSV files.
105
+
106
+ ## Notebook API
107
+
108
+ The easiest way to work from Jupyter is the high-level `Studio` interface:
109
+
110
+ ```python
111
+ from ber_equalization_studio import Studio
112
+
113
+ studio = Studio(
114
+ data_dirs=["../test"],
115
+ out_dir="notebook_runs",
116
+ device="cuda",
117
+ )
118
+
119
+ studio.models()
120
+ ```
121
+
122
+ Run one experiment:
123
+
124
+ ```python
125
+ run = studio.run(
126
+ name="fastkan_smoke",
127
+ models=["complex_fastkan", "mlp"],
128
+ epochs=5,
129
+ lr=1e-3,
130
+ context_k=32,
131
+ max_test_files=1,
132
+ )
133
+
134
+ run.results
135
+ ```
136
+
137
+ Useful result helpers:
138
+
139
+ ```python
140
+ run.best()
141
+ run.compare()
142
+ run.history("complex_fastkan")
143
+ run.plot_history("complex_fastkan")
144
+ run.plot_comparison()
145
+ run.run_dir
146
+ ```
147
+
148
+ Sweep a small research grid:
149
+
150
+ ```python
151
+ study = studio.sweep(
152
+ name="context_lr_sweep",
153
+ models=["complex_fastkan"],
154
+ grid={
155
+ "context_k": [16, 32, 64],
156
+ "lr": [1e-3, 3e-4],
157
+ },
158
+ epochs=50,
159
+ max_test_files=2,
160
+ )
161
+
162
+ study.results.sort_values("equalized_ber")
163
+ study.best()
164
+ study.plot_tradeoff(x="trainable_params", y="equalized_ber")
165
+ ```
166
+
167
+ Short parameters such as `epochs`, `lr`, `context_k`, `max_test_files`, `data_dirs`,
168
+ and `out_dir` are mapped to the structured config. For advanced settings, use dotted
169
+ keys:
170
+
171
+ ```python
172
+ run = studio.run(
173
+ name="custom_eval",
174
+ models="complex_fastkan",
175
+ **{
176
+ "evaluation.ber_scale_steps": 20,
177
+ "training.early_stopping_patience": 100,
178
+ },
179
+ )
180
+ ```
181
+
182
+ ## Low-Level Python API
183
+
184
+ ```python
185
+ from pathlib import Path
186
+
187
+ from ber_equalization_studio import (
188
+ DataConfig,
189
+ ExperimentConfig,
190
+ ExperimentRunner,
191
+ OutputConfig,
192
+ StudioConfig,
193
+ TrainingConfig,
194
+ )
195
+
196
+ config = StudioConfig(
197
+ data=DataConfig(
198
+ data_dirs=[Path("../BER_minimization_survey/symbols_new")],
199
+ context_k=32,
200
+ max_test_files=1,
201
+ ),
202
+ training=TrainingConfig(epochs=10, learning_rate=1e-3),
203
+ output=OutputConfig(out_dir=Path("studio_outputs"), experiment_name="fastkan_baseline"),
204
+ experiment=ExperimentConfig(models=["complex_fastkan", "efficient_kan_baseline", "mlp"]),
205
+ )
206
+
207
+ result = ExperimentRunner(config).run()
208
+ print(result["results_csv"])
209
+ ```
210
+
211
+ ## Available Models
212
+
213
+ Use:
214
+
215
+ ```powershell
216
+ ber-studio models
217
+ ```
218
+
219
+ Important current models:
220
+
221
+ - `complex_fastkan`: lightweight complex temporal encoder + RBF/FastKAN regression head.
222
+ - `complex_fastkan_classifier`: same encoder + RBF/FastKAN 16-class detector.
223
+ - `efficient_kan_baseline`: flat IQ window + B-spline EfficientKAN regression.
224
+ - `kan_classifier`: flat IQ window + B-spline EfficientKAN classifier.
225
+ - `cnn_kan`: temporal CNN + EfficientKAN head.
226
+ - `mlp`, `cnn`, `tcn`, `lstm`, `transformer`: neural baselines.
227
+
228
+ ## Outputs
229
+
230
+ Each run creates:
231
+
232
+ ```text
233
+ studio_outputs/<experiment_name>/
234
+ config.json
235
+ dataset_summary.json
236
+ results.csv
237
+ model_comparison_ber.html
238
+ model_comparison_complexity.html
239
+ <model_name>/
240
+ metrics.json
241
+ history.json
242
+ final_state_dict.pt
243
+ <model_name>_loss.html
244
+ <model_name>_ber.html
245
+ ```
246
+
247
+ Compare previous runs:
248
+
249
+ ```powershell
250
+ ber-studio compare studio_outputs
251
+ ```
252
+
253
+ ## Research Workflow
254
+
255
+ Recommended progression:
256
+
257
+ 1. In Jupyter, start with `Studio(...).models()` to choose candidate models.
258
+ 2. Run `complex_fastkan`, `efficient_kan_baseline`, and `mlp` as a short smoke test.
259
+ 3. Increase epochs and use the full test split for promising candidates.
260
+ 4. Use `run.results`, `study.results`, and saved `results.csv` files for paper tables.
261
+ 5. Sweep one family at a time with `studio.sweep(...)`, changing only a few parameters per study.
262
+
263
+ The current implementation intentionally keeps the original backend intact. That means
264
+ existing BER computation, Gray labels, file-level split protocol, normalization, KAN
265
+ implementations, and training loop behavior are preserved while the new API makes
266
+ future experiments much easier to compose.
@@ -0,0 +1,231 @@
1
+ # BER Equalization Studio
2
+
3
+ Research library for BER equalization experiments in nonlinear fiber-optic links.
4
+
5
+ The studio wraps the existing, validated `BER_minimization_survey/ber_equalization.py`
6
+ backend with a cleaner API:
7
+
8
+ - structured dataclass configs instead of editing one large global `Config`;
9
+ - model registry and aliases;
10
+ - reusable experiment runner;
11
+ - per-model run folders with checkpoints, metrics, histories, and plots;
12
+ - result comparison helpers for paper tables;
13
+ - Plotly HTML visualizations for BER/loss curves and model tradeoffs.
14
+
15
+ ## Layout
16
+
17
+ ```text
18
+ ber-equalization-studio/
19
+ src/ber_equalization_studio/
20
+ config.py # DataConfig, ModelConfig, TrainingConfig, StudioConfig
21
+ legacy.py # adapter to the original ber_equalization.py backend
22
+ models.py # model registry and constructors
23
+ data.py # dataset preparation wrapper
24
+ experiment.py # ExperimentRunner
25
+ api.py # notebook-friendly Studio, RunResult, StudyResult
26
+ results.py # result loading/comparison helpers
27
+ visualization.py # BER/loss/complexity plots
28
+ cli.py # ber-studio command
29
+ examples/
30
+ run_smoke.py
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ Install from PyPI:
36
+
37
+ ```powershell
38
+ python -m pip install "ber-equalization-studio[notebook]"
39
+ ```
40
+
41
+ For local development from the repository root:
42
+
43
+ ```powershell
44
+ cd ber-equalization-studio
45
+ python -m pip install -e ".[notebook,dev]"
46
+ ber-studio models
47
+ ```
48
+
49
+ Run a short smoke experiment:
50
+
51
+ ```powershell
52
+ ber-studio run `
53
+ --name smoke_complex_fastkan `
54
+ --models complex_fastkan,mlp `
55
+ --data-dir ..\BER_minimization_survey\symbols_new `
56
+ --epochs 5 `
57
+ --max-test-files 1
58
+ ```
59
+
60
+ If your symbol CSV files live directly under `BER_minimization_survey`, pass that
61
+ folder instead. The backend searches for files named:
62
+
63
+ ```text
64
+ Symbols_1m_1ch_PR_*.csv
65
+ ```
66
+
67
+ The PyPI package includes the legacy training backend and the local EfficientKAN
68
+ implementation used by the studio. You only need to pass data directories with symbol
69
+ CSV files.
70
+
71
+ ## Notebook API
72
+
73
+ The easiest way to work from Jupyter is the high-level `Studio` interface:
74
+
75
+ ```python
76
+ from ber_equalization_studio import Studio
77
+
78
+ studio = Studio(
79
+ data_dirs=["../test"],
80
+ out_dir="notebook_runs",
81
+ device="cuda",
82
+ )
83
+
84
+ studio.models()
85
+ ```
86
+
87
+ Run one experiment:
88
+
89
+ ```python
90
+ run = studio.run(
91
+ name="fastkan_smoke",
92
+ models=["complex_fastkan", "mlp"],
93
+ epochs=5,
94
+ lr=1e-3,
95
+ context_k=32,
96
+ max_test_files=1,
97
+ )
98
+
99
+ run.results
100
+ ```
101
+
102
+ Useful result helpers:
103
+
104
+ ```python
105
+ run.best()
106
+ run.compare()
107
+ run.history("complex_fastkan")
108
+ run.plot_history("complex_fastkan")
109
+ run.plot_comparison()
110
+ run.run_dir
111
+ ```
112
+
113
+ Sweep a small research grid:
114
+
115
+ ```python
116
+ study = studio.sweep(
117
+ name="context_lr_sweep",
118
+ models=["complex_fastkan"],
119
+ grid={
120
+ "context_k": [16, 32, 64],
121
+ "lr": [1e-3, 3e-4],
122
+ },
123
+ epochs=50,
124
+ max_test_files=2,
125
+ )
126
+
127
+ study.results.sort_values("equalized_ber")
128
+ study.best()
129
+ study.plot_tradeoff(x="trainable_params", y="equalized_ber")
130
+ ```
131
+
132
+ Short parameters such as `epochs`, `lr`, `context_k`, `max_test_files`, `data_dirs`,
133
+ and `out_dir` are mapped to the structured config. For advanced settings, use dotted
134
+ keys:
135
+
136
+ ```python
137
+ run = studio.run(
138
+ name="custom_eval",
139
+ models="complex_fastkan",
140
+ **{
141
+ "evaluation.ber_scale_steps": 20,
142
+ "training.early_stopping_patience": 100,
143
+ },
144
+ )
145
+ ```
146
+
147
+ ## Low-Level Python API
148
+
149
+ ```python
150
+ from pathlib import Path
151
+
152
+ from ber_equalization_studio import (
153
+ DataConfig,
154
+ ExperimentConfig,
155
+ ExperimentRunner,
156
+ OutputConfig,
157
+ StudioConfig,
158
+ TrainingConfig,
159
+ )
160
+
161
+ config = StudioConfig(
162
+ data=DataConfig(
163
+ data_dirs=[Path("../BER_minimization_survey/symbols_new")],
164
+ context_k=32,
165
+ max_test_files=1,
166
+ ),
167
+ training=TrainingConfig(epochs=10, learning_rate=1e-3),
168
+ output=OutputConfig(out_dir=Path("studio_outputs"), experiment_name="fastkan_baseline"),
169
+ experiment=ExperimentConfig(models=["complex_fastkan", "efficient_kan_baseline", "mlp"]),
170
+ )
171
+
172
+ result = ExperimentRunner(config).run()
173
+ print(result["results_csv"])
174
+ ```
175
+
176
+ ## Available Models
177
+
178
+ Use:
179
+
180
+ ```powershell
181
+ ber-studio models
182
+ ```
183
+
184
+ Important current models:
185
+
186
+ - `complex_fastkan`: lightweight complex temporal encoder + RBF/FastKAN regression head.
187
+ - `complex_fastkan_classifier`: same encoder + RBF/FastKAN 16-class detector.
188
+ - `efficient_kan_baseline`: flat IQ window + B-spline EfficientKAN regression.
189
+ - `kan_classifier`: flat IQ window + B-spline EfficientKAN classifier.
190
+ - `cnn_kan`: temporal CNN + EfficientKAN head.
191
+ - `mlp`, `cnn`, `tcn`, `lstm`, `transformer`: neural baselines.
192
+
193
+ ## Outputs
194
+
195
+ Each run creates:
196
+
197
+ ```text
198
+ studio_outputs/<experiment_name>/
199
+ config.json
200
+ dataset_summary.json
201
+ results.csv
202
+ model_comparison_ber.html
203
+ model_comparison_complexity.html
204
+ <model_name>/
205
+ metrics.json
206
+ history.json
207
+ final_state_dict.pt
208
+ <model_name>_loss.html
209
+ <model_name>_ber.html
210
+ ```
211
+
212
+ Compare previous runs:
213
+
214
+ ```powershell
215
+ ber-studio compare studio_outputs
216
+ ```
217
+
218
+ ## Research Workflow
219
+
220
+ Recommended progression:
221
+
222
+ 1. In Jupyter, start with `Studio(...).models()` to choose candidate models.
223
+ 2. Run `complex_fastkan`, `efficient_kan_baseline`, and `mlp` as a short smoke test.
224
+ 3. Increase epochs and use the full test split for promising candidates.
225
+ 4. Use `run.results`, `study.results`, and saved `results.csv` files for paper tables.
226
+ 5. Sweep one family at a time with `studio.sweep(...)`, changing only a few parameters per study.
227
+
228
+ The current implementation intentionally keeps the original backend intact. That means
229
+ existing BER computation, Gray labels, file-level split protocol, normalization, KAN
230
+ implementations, and training loop behavior are preserved while the new API makes
231
+ future experiments much easier to compose.
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "ber-equalization-studio"
3
+ version = "0.1.0"
4
+ description = "Research studio for BER equalization experiments in nonlinear optical links."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ keywords = ["ber", "equalization", "optical-communications", "photonics", "pytorch"]
8
+ dependencies = [
9
+ "matplotlib>=3.8",
10
+ "numpy>=1.24",
11
+ "pandas>=2.0",
12
+ "plotly>=5.18",
13
+ "torch>=2.1",
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Science/Research",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
23
+ "Topic :: Scientific/Engineering :: Physics",
24
+ ]
25
+
26
+ [project.optional-dependencies]
27
+ kan = []
28
+ notebook = [
29
+ "ipykernel>=6.29",
30
+ "ipython>=8.18",
31
+ "ipywidgets>=8.1",
32
+ "jupyterlab>=4.0",
33
+ "nbformat>=5.9",
34
+ ]
35
+ dev = [
36
+ "build>=1.2",
37
+ "pytest>=8.0",
38
+ "twine>=5.1",
39
+ ]
40
+ publish = [
41
+ "build>=1.2",
42
+ "twine>=5.1",
43
+ ]
44
+
45
+ [project.scripts]
46
+ ber-studio = "ber_equalization_studio.cli:main"
47
+
48
+ [build-system]
49
+ requires = ["setuptools>=68", "wheel"]
50
+ build-backend = "setuptools.build_meta"
51
+
52
+ [tool.setuptools.packages.find]
53
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,58 @@
1
+ """Convenient research API for BER equalization experiments."""
2
+
3
+ from ber_equalization_studio.config import (
4
+ DataConfig,
5
+ EvaluationConfig,
6
+ ExperimentConfig,
7
+ ModelConfig,
8
+ OutputConfig,
9
+ StudioConfig,
10
+ TrainingConfig,
11
+ )
12
+
13
+ __all__ = [
14
+ "DataConfig",
15
+ "EvaluationConfig",
16
+ "ExperimentConfig",
17
+ "ExperimentRunner",
18
+ "ModelConfig",
19
+ "OutputConfig",
20
+ "RunResult",
21
+ "Studio",
22
+ "StudioConfig",
23
+ "StudyResult",
24
+ "TrainingConfig",
25
+ "available_models",
26
+ "build_model",
27
+ "compare_results",
28
+ "load_result_table",
29
+ "run",
30
+ "run_experiment",
31
+ "sweep",
32
+ ]
33
+
34
+
35
+ def __getattr__(name):
36
+ if name in {"ExperimentRunner", "run_experiment"}:
37
+ from ber_equalization_studio.experiment import ExperimentRunner, run_experiment
38
+
39
+ return {"ExperimentRunner": ExperimentRunner, "run_experiment": run_experiment}[name]
40
+ if name in {"available_models", "build_model"}:
41
+ from ber_equalization_studio.models import available_models, build_model
42
+
43
+ return {"available_models": available_models, "build_model": build_model}[name]
44
+ if name in {"compare_results", "load_result_table"}:
45
+ from ber_equalization_studio.results import compare_results, load_result_table
46
+
47
+ return {"compare_results": compare_results, "load_result_table": load_result_table}[name]
48
+ if name in {"RunResult", "Studio", "StudyResult", "run", "sweep"}:
49
+ from ber_equalization_studio.api import RunResult, Studio, StudyResult, run, sweep
50
+
51
+ return {
52
+ "RunResult": RunResult,
53
+ "Studio": Studio,
54
+ "StudyResult": StudyResult,
55
+ "run": run,
56
+ "sweep": sweep,
57
+ }[name]
58
+ raise AttributeError(name)
@@ -0,0 +1 @@
1
+ """Packaged legacy backend used by the public studio API."""