edrft 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.
@@ -0,0 +1,25 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use edRFT in academic work, please cite this software and the associated article."
3
+ title: "edRFT"
4
+ version: "0.1.0"
5
+ date-released: "2026-06-25"
6
+ authors:
7
+ - family-names: "Bhambu"
8
+ given-names: "Aryan"
9
+ license: "MIT"
10
+ repository-code: "https://github.com/statsdl/edRFT"
11
+ preferred-citation:
12
+ type: article
13
+ title: "Deep random vector functional link transformer network with multiple output layers for significant wave height forecasting"
14
+ authors:
15
+ - family-names: "Bhambu"
16
+ given-names: "Aryan"
17
+ - family-names: "Gao"
18
+ given-names: "Ruobin"
19
+ - family-names: "Suganthan"
20
+ given-names: "Ponnuthurai Nagaratnam"
21
+ - family-names: "Natarajan"
22
+ given-names: "Selvaraju"
23
+ journal: "Applied Soft Computing"
24
+ year: 2025
25
+ start: "114136"
edrft-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aryan Bhambu
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,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ include CITATION.cff
4
+ include requirements.txt
5
+ recursive-include examples *.py
6
+ recursive-include docs *.rst
edrft-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,166 @@
1
+ Metadata-Version: 2.4
2
+ Name: edrft
3
+ Version: 0.1.0
4
+ Summary: RFT and edRFT models for significant wave-height time-series forecasting.
5
+ Author: Aryan Bhambu
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/statsdl/edRFT
8
+ Project-URL: Issues, https://github.com/statsdl/edRFT/issues
9
+ Keywords: edrft,rft,rvfl,transformer,wave-height,forecasting
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: numpy>=1.21
22
+ Requires-Dist: torch>=2.0
23
+ Provides-Extra: tuning
24
+ Requires-Dist: hyperopt>=0.2.7; extra == "tuning"
25
+ Requires-Dist: setuptools<81; extra == "tuning"
26
+ Provides-Extra: wave
27
+ Requires-Dist: hyperopt>=0.2.7; extra == "wave"
28
+ Requires-Dist: pandas>=1.3; extra == "wave"
29
+ Requires-Dist: setuptools<81; extra == "wave"
30
+ Provides-Extra: dev
31
+ Requires-Dist: build; extra == "dev"
32
+ Requires-Dist: hyperopt>=0.2.7; extra == "dev"
33
+ Requires-Dist: pandas>=1.3; extra == "dev"
34
+ Requires-Dist: pytest; extra == "dev"
35
+ Requires-Dist: setuptools<81; extra == "dev"
36
+ Requires-Dist: twine; extra == "dev"
37
+ Dynamic: license-file
38
+
39
+ # edRFT
40
+
41
+ `edrft` provides Random Vector Functional Link Transformer models for
42
+ significant wave-height forecasting:
43
+
44
+ - `RFTRegressor`: a shallow randomized transformer encoder with ridge readout.
45
+ - `EDRFTRegressor`: an ensemble deep RFT with one output layer per hidden layer.
46
+ - Hyperopt/TPE tuning using the default edRFT search ranges.
47
+ - NDBC wave forecasting experiment helpers that do not write result artifacts.
48
+
49
+ The public package uses the model naming: `RFT` and `edRFT`. Older `rft` and
50
+ `edrft` script names are retained only under legacy files for traceability.
51
+
52
+ ## Installation
53
+
54
+ Core install:
55
+
56
+ ```bash
57
+ git clone https://github.com/statsdl/edRFT.git
58
+ cd edRFT
59
+ pip install .
60
+ ```
61
+
62
+ Wave experiment dependencies:
63
+
64
+ ```bash
65
+ pip install ".[wave]"
66
+ ```
67
+
68
+ Development:
69
+
70
+ ```bash
71
+ pip install -e ".[dev]"
72
+ pytest
73
+ ```
74
+
75
+ ## Quick Start
76
+
77
+ ```python
78
+ import numpy as np
79
+ from edrft import EDRFTRegressor, make_forecasting_frame
80
+
81
+ series = np.sin(np.linspace(0, 16, 240))
82
+ X, y = make_forecasting_frame(series, order=4)
83
+
84
+ model = EDRFTRegressor(n_layers=3, n_hidden=32, random_state=0)
85
+ model.fit(X[:180], y[:180])
86
+ pred = model.predict(X[180:])
87
+ ```
88
+
89
+ ## Wave Forecasting Example
90
+
91
+ ```bash
92
+ python examples/run_wave_forecasting.py \
93
+ --data-dir wave \
94
+ --stations 46001h \
95
+ --years 2017 \
96
+ --seeds 0 \
97
+ --look-back 48 \
98
+ --horizon 4 \
99
+ --layers 10 \
100
+ --max-evals 100
101
+ ```
102
+
103
+ The runner prints metrics to stdout only. It follows the original scripts:
104
+
105
+ - NDBC features: `WDIR`, `WSPD`, `GST`, `APD`, `WVHT`
106
+ - Missing sentinel cleanup
107
+ - Default look-back window: 48
108
+ - Default forecasting horizon: 4
109
+ - Min-max scaling to `[-1, 1]`
110
+ - Chronological split: 70% train, 10% validation, 20% test
111
+ - Hyperopt/TPE tuning with 100 evaluations by default
112
+ - Train+validation final fit
113
+ - RMSE, MAPE, MASE, and timing output
114
+
115
+ ## Hyperopt Tuning
116
+
117
+ ```python
118
+ from edrft.tuning import layerwise_tune_edrft
119
+
120
+ result = layerwise_tune_edrft(
121
+ X,
122
+ y,
123
+ n_layers=10,
124
+ validation_fraction=0.1 / 0.8,
125
+ max_evals=100,
126
+ random_state=0,
127
+ )
128
+ ```
129
+
130
+ ## Repository Notes
131
+
132
+ Supported package code lives in `src/edrft`.
133
+
134
+ The `legacy/`, `DeepRVFL_/`, `ForecastLib.py`, and old experiment scripts are
135
+ retained for traceability. They are not included in the PyPI wheel and are not
136
+ the supported package API.
137
+
138
+ ## PyPI Release
139
+
140
+ The publish workflow uses PyPI Trusted Publishing. Configure the PyPI trusted
141
+ publisher with:
142
+
143
+ - owner: `statsdl`
144
+ - repository: `edRFT`
145
+ - workflow: `publish.yml`
146
+ - environment: `pypi`
147
+
148
+ ## License
149
+
150
+ MIT
151
+
152
+ ## Reference
153
+
154
+ If you use edRFT in your work, please cite:
155
+
156
+ ```bibtex
157
+ @article{bhambu2025deep,
158
+ title={Deep random vector functional link transformer network with multiple output layers for significant wave height forecasting},
159
+ author={Bhambu, Aryan and Gao, Ruobin and Suganthan, Ponnuthurai Nagaratnam and Selvaraju, Natarajan},
160
+ journal={Applied Soft Computing},
161
+ pages={114136},
162
+ year={2025},
163
+ publisher={Elsevier}
164
+ }
165
+ ```
166
+
edrft-0.1.0/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # edRFT
2
+
3
+ `edrft` provides Random Vector Functional Link Transformer models for
4
+ significant wave-height forecasting:
5
+
6
+ - `RFTRegressor`: a shallow randomized transformer encoder with ridge readout.
7
+ - `EDRFTRegressor`: an ensemble deep RFT with one output layer per hidden layer.
8
+ - Hyperopt/TPE tuning using the default edRFT search ranges.
9
+ - NDBC wave forecasting experiment helpers that do not write result artifacts.
10
+
11
+ The public package uses the model naming: `RFT` and `edRFT`. Older `rft` and
12
+ `edrft` script names are retained only under legacy files for traceability.
13
+
14
+ ## Installation
15
+
16
+ Core install:
17
+
18
+ ```bash
19
+ git clone https://github.com/statsdl/edRFT.git
20
+ cd edRFT
21
+ pip install .
22
+ ```
23
+
24
+ Wave experiment dependencies:
25
+
26
+ ```bash
27
+ pip install ".[wave]"
28
+ ```
29
+
30
+ Development:
31
+
32
+ ```bash
33
+ pip install -e ".[dev]"
34
+ pytest
35
+ ```
36
+
37
+ ## Quick Start
38
+
39
+ ```python
40
+ import numpy as np
41
+ from edrft import EDRFTRegressor, make_forecasting_frame
42
+
43
+ series = np.sin(np.linspace(0, 16, 240))
44
+ X, y = make_forecasting_frame(series, order=4)
45
+
46
+ model = EDRFTRegressor(n_layers=3, n_hidden=32, random_state=0)
47
+ model.fit(X[:180], y[:180])
48
+ pred = model.predict(X[180:])
49
+ ```
50
+
51
+ ## Wave Forecasting Example
52
+
53
+ ```bash
54
+ python examples/run_wave_forecasting.py \
55
+ --data-dir wave \
56
+ --stations 46001h \
57
+ --years 2017 \
58
+ --seeds 0 \
59
+ --look-back 48 \
60
+ --horizon 4 \
61
+ --layers 10 \
62
+ --max-evals 100
63
+ ```
64
+
65
+ The runner prints metrics to stdout only. It follows the original scripts:
66
+
67
+ - NDBC features: `WDIR`, `WSPD`, `GST`, `APD`, `WVHT`
68
+ - Missing sentinel cleanup
69
+ - Default look-back window: 48
70
+ - Default forecasting horizon: 4
71
+ - Min-max scaling to `[-1, 1]`
72
+ - Chronological split: 70% train, 10% validation, 20% test
73
+ - Hyperopt/TPE tuning with 100 evaluations by default
74
+ - Train+validation final fit
75
+ - RMSE, MAPE, MASE, and timing output
76
+
77
+ ## Hyperopt Tuning
78
+
79
+ ```python
80
+ from edrft.tuning import layerwise_tune_edrft
81
+
82
+ result = layerwise_tune_edrft(
83
+ X,
84
+ y,
85
+ n_layers=10,
86
+ validation_fraction=0.1 / 0.8,
87
+ max_evals=100,
88
+ random_state=0,
89
+ )
90
+ ```
91
+
92
+ ## Repository Notes
93
+
94
+ Supported package code lives in `src/edrft`.
95
+
96
+ The `legacy/`, `DeepRVFL_/`, `ForecastLib.py`, and old experiment scripts are
97
+ retained for traceability. They are not included in the PyPI wheel and are not
98
+ the supported package API.
99
+
100
+ ## PyPI Release
101
+
102
+ The publish workflow uses PyPI Trusted Publishing. Configure the PyPI trusted
103
+ publisher with:
104
+
105
+ - owner: `statsdl`
106
+ - repository: `edRFT`
107
+ - workflow: `publish.yml`
108
+ - environment: `pypi`
109
+
110
+ ## License
111
+
112
+ MIT
113
+
114
+ ## Reference
115
+
116
+ If you use edRFT in your work, please cite:
117
+
118
+ ```bibtex
119
+ @article{bhambu2025deep,
120
+ title={Deep random vector functional link transformer network with multiple output layers for significant wave height forecasting},
121
+ author={Bhambu, Aryan and Gao, Ruobin and Suganthan, Ponnuthurai Nagaratnam and Selvaraju, Natarajan},
122
+ journal={Applied Soft Computing},
123
+ pages={114136},
124
+ year={2025},
125
+ publisher={Elsevier}
126
+ }
127
+ ```
128
+
@@ -0,0 +1,18 @@
1
+ edRFT
2
+ =====
3
+
4
+ ``edrft`` provides RFT and edRFT regressors for significant wave-height
5
+ forecasting.
6
+
7
+ Installation
8
+ ------------
9
+
10
+ .. code-block:: bash
11
+
12
+ pip install edrft
13
+
14
+ Optional wave experiment dependencies:
15
+
16
+ .. code-block:: bash
17
+
18
+ pip install "edrft[wave]"
@@ -0,0 +1,48 @@
1
+ import argparse
2
+
3
+ from edrft.wave import run_wave_experiment
4
+
5
+
6
+ def parse_csv(value):
7
+ return tuple(item.strip() for item in value.split(",") if item.strip())
8
+
9
+
10
+ def parse_seeds(value):
11
+ return tuple(int(item) for item in parse_csv(value))
12
+
13
+
14
+ def main():
15
+ parser = argparse.ArgumentParser(description="Run RFT/edRFT wave forecasting experiments.")
16
+ parser.add_argument("--data-dir", default="wave")
17
+ parser.add_argument("--stations", default="46001h")
18
+ parser.add_argument("--years", default="2017")
19
+ parser.add_argument("--seeds", default="0")
20
+ parser.add_argument("--look-back", type=int, default=48)
21
+ parser.add_argument("--order", type=int, default=None, help=argparse.SUPPRESS)
22
+ parser.add_argument("--horizon", type=int, default=4)
23
+ parser.add_argument("--layers", type=int, default=10)
24
+ parser.add_argument("--max-evals", type=int, default=100)
25
+ args = parser.parse_args()
26
+
27
+ results = run_wave_experiment(
28
+ data_dir=args.data_dir,
29
+ stations=parse_csv(args.stations),
30
+ years=parse_csv(args.years),
31
+ seeds=parse_seeds(args.seeds),
32
+ look_back=args.order if args.order is not None else args.look_back,
33
+ horizon=args.horizon,
34
+ n_layers=args.layers,
35
+ max_evals=args.max_evals,
36
+ )
37
+ for result in results:
38
+ print(
39
+ f"{result.year} {result.station} seed={result.seed} {result.model:5s} "
40
+ f"RMSE={result.rmse:.6f} MAPE={result.mape:.6f} MASE={result.mase:.6f} "
41
+ f"tune={result.tuning_seconds:.3f}s train={result.training_seconds:.3f}s "
42
+ f"test={result.testing_seconds:.3f}s"
43
+ )
44
+ print(f"best_params={result.best_params}")
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()
@@ -0,0 +1,69 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "edrft"
7
+ version = "0.1.0"
8
+ description = "RFT and edRFT models for significant wave-height time-series forecasting."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [
14
+ {name = "Aryan Bhambu"},
15
+ ]
16
+ keywords = ["edrft", "rft", "rvfl", "transformer", "wave-height", "forecasting"]
17
+ classifiers = [
18
+ "Development Status :: 3 - Alpha",
19
+ "Intended Audience :: Science/Research",
20
+ "Operating System :: OS Independent",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
26
+ ]
27
+ dependencies = [
28
+ "numpy>=1.21",
29
+ "torch>=2.0",
30
+ ]
31
+
32
+ [project.optional-dependencies]
33
+ tuning = [
34
+ "hyperopt>=0.2.7",
35
+ "setuptools<81",
36
+ ]
37
+ wave = [
38
+ "hyperopt>=0.2.7",
39
+ "pandas>=1.3",
40
+ "setuptools<81",
41
+ ]
42
+ dev = [
43
+ "build",
44
+ "hyperopt>=0.2.7",
45
+ "pandas>=1.3",
46
+ "pytest",
47
+ "setuptools<81",
48
+ "twine",
49
+ ]
50
+
51
+ [project.urls]
52
+ Homepage = "https://github.com/statsdl/edRFT"
53
+ Issues = "https://github.com/statsdl/edRFT/issues"
54
+
55
+ [tool.setuptools.packages.find]
56
+ where = ["src"]
57
+
58
+ [tool.black]
59
+ line-length = 99
60
+ target-version = ["py38"]
61
+
62
+ [tool.isort]
63
+ profile = "black"
64
+ multi_line_output = 3
65
+
66
+ [tool.pytest.ini_options]
67
+ minversion = "6.0"
68
+ addopts = "-ra -q"
69
+ testpaths = ["tests"]
@@ -0,0 +1,2 @@
1
+ numpy>=1.21
2
+ torch>=2.0
edrft-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
edrft-0.1.0/setup.py ADDED
@@ -0,0 +1,4 @@
1
+ from setuptools import setup
2
+
3
+
4
+ setup()
@@ -0,0 +1,18 @@
1
+ """Random Vector Functional Link Transformer models."""
2
+
3
+ from .data import chronological_split, make_forecasting_frame
4
+ from .metrics import mean_absolute_scaled_error, mean_absolute_percentage_error, root_mean_squared_error
5
+ from .models import EDRFTRegressor, RFTLayerParams, RFTRegressor
6
+
7
+ __all__ = [
8
+ "EDRFTRegressor",
9
+ "RFTLayerParams",
10
+ "RFTRegressor",
11
+ "chronological_split",
12
+ "make_forecasting_frame",
13
+ "mean_absolute_percentage_error",
14
+ "mean_absolute_scaled_error",
15
+ "root_mean_squared_error",
16
+ ]
17
+
18
+ __version__ = "0.1.0"
@@ -0,0 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import Iterable
5
+
6
+ import numpy as np
7
+
8
+
9
+ def make_forecasting_frame(
10
+ series: Iterable[float] | np.ndarray,
11
+ order: int = 48,
12
+ horizon: int = 4,
13
+ ) -> tuple[np.ndarray, np.ndarray]:
14
+ """Convert a univariate or multivariate sequence into lagged samples."""
15
+
16
+ values = np.asarray(series, dtype=np.float32)
17
+ if values.ndim == 1:
18
+ values = values.reshape(-1, 1)
19
+ if values.ndim != 2:
20
+ raise ValueError("series must be a 1D or 2D array.")
21
+ if order <= 0 or horizon <= 0:
22
+ raise ValueError("order and horizon must be positive.")
23
+ n_samples = values.shape[0] - order - horizon + 1
24
+ if n_samples <= 0:
25
+ raise ValueError("series is too short for the requested order and horizon.")
26
+ X = np.zeros((n_samples, values.shape[1] * order), dtype=np.float32)
27
+ y = np.zeros((n_samples, values.shape[1]), dtype=np.float32)
28
+ for i in range(n_samples):
29
+ X[i] = values[i : i + order].ravel()
30
+ y[i] = values[i + order + horizon - 1]
31
+ return X, y.ravel() if y.shape[1] == 1 else y
32
+
33
+
34
+ def chronological_split(n_samples: int, validation_fraction: float = 0.1, test_fraction: float = 0.2):
35
+ """Return train, validation, full-train, and test indexes in time order."""
36
+
37
+ test_len = int(test_fraction * n_samples)
38
+ val_len = int(validation_fraction * n_samples)
39
+ train_len = n_samples - val_len - test_len
40
+ if train_len <= 0:
41
+ raise ValueError("Not enough samples for the requested split.")
42
+ train = np.arange(train_len)
43
+ val = np.arange(train_len, train_len + val_len)
44
+ full_train = np.arange(train_len + val_len)
45
+ test = np.arange(train_len + val_len, n_samples)
46
+ return train, val, full_train, test
47
+
48
+
49
+ def load_ndbc_wave_file(path: str | Path, features: list[str] | None = None) -> pd.DataFrame:
50
+ """Load an NDBC wave-height text file and clean sentinel missing values."""
51
+
52
+ import pandas as pd
53
+
54
+ features = features or ["WDIR", "WSPD", "GST", "APD", "WVHT"]
55
+ frame = pd.read_csv(path, sep=r"\s+", compression="infer")
56
+ frame = frame[features].replace(["99.0", "99.00", 99.0, 99.00], np.nan)
57
+ return frame.ffill().bfill().astype(float)
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+
5
+
6
+ def root_mean_squared_error(y_true, y_pred) -> float:
7
+ truth = np.asarray(y_true, dtype=float).ravel()
8
+ pred = np.asarray(y_pred, dtype=float).ravel()
9
+ return float(np.sqrt(np.mean((truth - pred) ** 2)))
10
+
11
+
12
+ def mean_absolute_percentage_error(y_true, y_pred, epsilon: float = 1e-8) -> float:
13
+ truth = np.asarray(y_true, dtype=float).ravel()
14
+ pred = np.asarray(y_pred, dtype=float).ravel()
15
+ denom = np.maximum(np.abs(truth), epsilon)
16
+ return float(np.mean(np.abs((truth - pred) / denom)))
17
+
18
+
19
+ def mean_absolute_scaled_error(y_true, y_pred, history, seasonality: int = 1) -> float:
20
+ truth = np.asarray(y_true, dtype=float).ravel()
21
+ pred = np.asarray(y_pred, dtype=float).ravel()
22
+ hist = np.asarray(history, dtype=float).ravel()
23
+ scale = np.mean(np.abs(hist[seasonality:] - hist[:-seasonality]))
24
+ if scale == 0:
25
+ return float("inf")
26
+ return float(np.mean(np.abs(truth - pred)) / scale)