blnetwork 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,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Yue Liang
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,9 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+
5
+ global-exclude *.pyc
6
+ global-exclude __pycache__/*
7
+ exclude .gitignore
8
+ prune .git
9
+ prune .github
@@ -0,0 +1,121 @@
1
+ Metadata-Version: 2.4
2
+ Name: blnetwork
3
+ Version: 0.1.0
4
+ Summary: Behavior Learning (BL): Learning Hierarchical Optimization Structures from Data
5
+ Author-email: Yue Liang <yue.liang@student.uni-tuebingen.de>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 Yue Liang
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+ Project-URL: Homepage, https://github.com/YueLiang-hye/Behavior-Learning
28
+ Project-URL: Issues, https://github.com/YueLiang-hye/Behavior-Learning/issues
29
+ Classifier: Programming Language :: Python :: 3
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Operating System :: OS Independent
32
+ Requires-Python: >=3.10.9
33
+ Description-Content-Type: text/markdown
34
+ License-File: LICENSE
35
+ Requires-Dist: torch>=2.2
36
+ Requires-Dist: numpy>=1.26
37
+ Requires-Dist: pandas>=2.1
38
+ Requires-Dist: scikit-learn>=1.3
39
+ Dynamic: license-file
40
+
41
+ # Behavior Learning (BL)
42
+
43
+ Behavior Learning (BL) is a general-purpose machine learning framework grounded in behavioral science. It unifies predictive performance and intrinsic interpretability within a single modeling paradigm. BL learns explicit optimization structures from data by parameterizing a compositional utility function built from interpretable modular blocks. Each block represents a Utility Maximization Problem (UMP), a foundational framework of decision-making and optimization. BL supports architectures ranging from a single UMP to hierarchical compositions, enabling expressive yet structurally transparent models. Unlike post-hoc explanation methods, BL provides interpretability by design while maintaining strong empirical performance on high-dimensional tasks.
44
+
45
+ ## Installation
46
+ blnetwork can be installed via PyPI or directly from GitHub.
47
+
48
+ **Pre-requisites:**
49
+
50
+ ```
51
+ Python 3.10.9 or higher
52
+ pip
53
+ ```
54
+
55
+ **For developers**
56
+
57
+ ```
58
+ git clone https://github.com/YueLiang-hye/Behavior-Learning.git
59
+ cd blnetwork
60
+ pip install -e .
61
+ ```
62
+
63
+ **Installation via github**
64
+
65
+ ```
66
+ pip install git+https://github.com/YueLiang-hye/Behavior-Learning.git
67
+ ```
68
+
69
+ **Installation via PyPI:**
70
+ ```
71
+ pip install blnetwork
72
+ ```
73
+
74
+ Requirements
75
+
76
+ ```python
77
+ # python==3.10.9
78
+ torch>=2.2
79
+ numpy>=1.26
80
+ pandas>=2.0
81
+ scikit-learn>=1.3
82
+ ```
83
+
84
+ After activating the virtual environment, you can install specific package requirements as follows:
85
+ ```python
86
+ pip install -r requirements.txt
87
+ ```
88
+
89
+ **Optional: Conda Environment Setup**
90
+ For those who prefer using Conda:
91
+ ```
92
+ conda create --name blnetwork-env python=3.10.9
93
+ conda activate blnetwork-env
94
+ pip install git+https://github.com/YueLiang-hye/Behavior-Learning.git # For GitHub installation
95
+ # or
96
+ pip install blnetwork # For PyPI installation
97
+ ```
98
+
99
+ ## Computation Requirements
100
+
101
+ BL is implemented in PyTorch and supports both CPU and GPU training.
102
+
103
+ - Small-scale tabular examples run on a single CPU within a few minutes.
104
+ - High-dimensional settings may benefit from GPU acceleration (e.g., NVIDIA L40).
105
+
106
+ For most tabular tasks, CPU training is sufficient.
107
+
108
+ ## Examples
109
+
110
+ Start with the notebooks in [`examples/`](./examples/):
111
+
112
+ - [Example 1: Boston Housing (continuous)](./examples/Example_1_boston_housing.ipynb)
113
+ - [Example 2: Breast Cancer (classification)](./examples/Example_2_breast_cancer.ipynb)
114
+
115
+ ## Advice on hyperparameter tuning
116
+ In many cases, BL can achieve comparable (or slightly better) performance than an MLP baseline using roughly one third of the hidden width.
117
+
118
+ Other hyperparameters can be initialized based on standard MLP tuning, and then refined for the specific task.
119
+
120
+ ## Contact
121
+ If you have any questions, please contact yue.liang@student.uni-tuebingen.de
@@ -0,0 +1,81 @@
1
+ # Behavior Learning (BL)
2
+
3
+ Behavior Learning (BL) is a general-purpose machine learning framework grounded in behavioral science. It unifies predictive performance and intrinsic interpretability within a single modeling paradigm. BL learns explicit optimization structures from data by parameterizing a compositional utility function built from interpretable modular blocks. Each block represents a Utility Maximization Problem (UMP), a foundational framework of decision-making and optimization. BL supports architectures ranging from a single UMP to hierarchical compositions, enabling expressive yet structurally transparent models. Unlike post-hoc explanation methods, BL provides interpretability by design while maintaining strong empirical performance on high-dimensional tasks.
4
+
5
+ ## Installation
6
+ blnetwork can be installed via PyPI or directly from GitHub.
7
+
8
+ **Pre-requisites:**
9
+
10
+ ```
11
+ Python 3.10.9 or higher
12
+ pip
13
+ ```
14
+
15
+ **For developers**
16
+
17
+ ```
18
+ git clone https://github.com/YueLiang-hye/Behavior-Learning.git
19
+ cd blnetwork
20
+ pip install -e .
21
+ ```
22
+
23
+ **Installation via github**
24
+
25
+ ```
26
+ pip install git+https://github.com/YueLiang-hye/Behavior-Learning.git
27
+ ```
28
+
29
+ **Installation via PyPI:**
30
+ ```
31
+ pip install blnetwork
32
+ ```
33
+
34
+ Requirements
35
+
36
+ ```python
37
+ # python==3.10.9
38
+ torch>=2.2
39
+ numpy>=1.26
40
+ pandas>=2.0
41
+ scikit-learn>=1.3
42
+ ```
43
+
44
+ After activating the virtual environment, you can install specific package requirements as follows:
45
+ ```python
46
+ pip install -r requirements.txt
47
+ ```
48
+
49
+ **Optional: Conda Environment Setup**
50
+ For those who prefer using Conda:
51
+ ```
52
+ conda create --name blnetwork-env python=3.10.9
53
+ conda activate blnetwork-env
54
+ pip install git+https://github.com/YueLiang-hye/Behavior-Learning.git # For GitHub installation
55
+ # or
56
+ pip install blnetwork # For PyPI installation
57
+ ```
58
+
59
+ ## Computation Requirements
60
+
61
+ BL is implemented in PyTorch and supports both CPU and GPU training.
62
+
63
+ - Small-scale tabular examples run on a single CPU within a few minutes.
64
+ - High-dimensional settings may benefit from GPU acceleration (e.g., NVIDIA L40).
65
+
66
+ For most tabular tasks, CPU training is sufficient.
67
+
68
+ ## Examples
69
+
70
+ Start with the notebooks in [`examples/`](./examples/):
71
+
72
+ - [Example 1: Boston Housing (continuous)](./examples/Example_1_boston_housing.ipynb)
73
+ - [Example 2: Breast Cancer (classification)](./examples/Example_2_breast_cancer.ipynb)
74
+
75
+ ## Advice on hyperparameter tuning
76
+ In many cases, BL can achieve comparable (or slightly better) performance than an MLP baseline using roughly one third of the hidden width.
77
+
78
+ Other hyperparameters can be initialized based on standard MLP tuning, and then refined for the specific task.
79
+
80
+ ## Contact
81
+ If you have any questions, please contact yue.liang@student.uni-tuebingen.de
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "blnetwork"
7
+ version = "0.1.0"
8
+ description = "Behavior Learning (BL): Learning Hierarchical Optimization Structures from Data"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10.9"
11
+ license = { file = "LICENSE" }
12
+ authors = [
13
+ { name = "Yue Liang", email = "yue.liang@student.uni-tuebingen.de" }
14
+ ]
15
+
16
+
17
+ dependencies = [
18
+ "torch>=2.2",
19
+ "numpy>=1.26",
20
+ "pandas>=2.1",
21
+ "scikit-learn>=1.3"
22
+ ]
23
+
24
+ classifiers = [
25
+ "Programming Language :: Python :: 3",
26
+ "License :: OSI Approved :: MIT License",
27
+ "Operating System :: OS Independent"
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/YueLiang-hye/Behavior-Learning"
32
+ Issues = "https://github.com/YueLiang-hye/Behavior-Learning/issues"
33
+
34
+ [tool.setuptools]
35
+ include-package-data = true
36
+
37
+ [tool.setuptools.package-dir]
38
+ "" = "src"
39
+
40
+ [tool.setuptools.packages.find]
41
+ where = ["src"]
42
+ include = ["blnetwork*"]
43
+
44
+ exclude = ["__pycache__*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,269 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List, Optional, Tuple, Dict
4
+
5
+ import numpy as np
6
+ import torch
7
+ import sys
8
+ import io
9
+ import torch.nn.functional as F
10
+
11
+ def _fmt_num(x: float, ndigits: int = 4) -> str:
12
+ s = f"{x:.{ndigits}f}"
13
+ s = s.rstrip("0").rstrip(".")
14
+ return s if s != "" else "0"
15
+
16
+
17
+ def _safe_numpy(t):
18
+ if t is None:
19
+ return None
20
+ if torch.is_tensor(t):
21
+ return t.detach().cpu().numpy()
22
+ return np.asarray(t)
23
+
24
+
25
+ def _get_bl_unit(block):
26
+ unit = getattr(block, "unit", None)
27
+ if unit is None:
28
+ raise AttributeError("Block has no attribute 'unit' (expected BLBlock.unit).")
29
+ return unit
30
+
31
+
32
+ def _get_lambdas(unit):
33
+ lam = unit.lam
34
+ if getattr(unit, "constrain_lambda", False):
35
+ eps = float(getattr(unit, "eps", 1e-8))
36
+ lam = F.softplus(lam) + eps
37
+
38
+ lam_u = _safe_numpy(lam[0])
39
+ lam_c = _safe_numpy(lam[1])
40
+ lam_t = _safe_numpy(lam[2])
41
+
42
+ return lam_u, lam_c, lam_t
43
+
44
+
45
+ def _get_backbone(model):
46
+ backbone = getattr(model, "backbone", None)
47
+ if backbone is not None:
48
+ return backbone
49
+ if hasattr(model, "blocks"):
50
+ return model
51
+ raise AttributeError("Cannot find backbone. Expected model.backbone or model.blocks.")
52
+
53
+
54
+ def _get_blocks(model) -> list:
55
+ backbone = _get_backbone(model)
56
+ blocks = getattr(backbone, "blocks", None)
57
+ if blocks is None:
58
+ raise AttributeError("Backbone has no attribute 'blocks'.")
59
+ return list(blocks)
60
+
61
+
62
+ def _get_hidden_dims(model) -> Optional[Tuple[int, ...]]:
63
+ backbone = _get_backbone(model)
64
+ v = getattr(backbone, "hidden_dims", None)
65
+ if v is None:
66
+ return None
67
+ return tuple(map(int, v))
68
+
69
+
70
+ def _get_output_linears(model) -> Dict[str, torch.nn.Linear]:
71
+ outs: Dict[str, torch.nn.Linear] = {}
72
+
73
+ lin = getattr(model, "linear_out", None)
74
+ if lin is not None and hasattr(lin, "weight"):
75
+ outs["Output Layer (Discrete)"] = lin
76
+ return outs
77
+
78
+ head = getattr(model, "head", None)
79
+ if head is None:
80
+ return outs
81
+
82
+ if isinstance(head, torch.nn.Linear):
83
+ outs["Output Layer"] = head
84
+ return outs
85
+
86
+ lin = getattr(head, "linear", None)
87
+ if lin is not None and hasattr(lin, "weight"):
88
+ outs["Output Layer"] = lin
89
+
90
+ return outs
91
+
92
+
93
+ def _emit_part_lines(
94
+ part_name: str,
95
+ lam_val: float,
96
+ w_row: np.ndarray,
97
+ b_val: float,
98
+ feature_names: List[str],
99
+ ndigits: int = 4,
100
+ tol: float = 0.0,
101
+ ) -> List[str]:
102
+ lines = []
103
+ lines.append(f"{part_name}")
104
+ lines.append(f"lambda {_fmt_num(float(lam_val), ndigits)}")
105
+
106
+ for name, w in zip(feature_names, w_row):
107
+ w = float(w)
108
+ if abs(w) > tol:
109
+ lines.append(f"----{name} {_fmt_num(w, ndigits)}")
110
+
111
+ if abs(float(b_val)) > tol:
112
+ lines.append(f"----C {_fmt_num(float(b_val), ndigits)}")
113
+
114
+ return lines
115
+
116
+
117
+ def _print_blocks(
118
+ block,
119
+ feature_names: List[str],
120
+ layer_idx: int,
121
+ ndigits: int = 4,
122
+ tol: float = 0.0,
123
+ ) -> int:
124
+
125
+ unit = _get_bl_unit(block)
126
+
127
+ num_basis = int(unit.lin_u.out_features)
128
+
129
+ lam_u, lam_c, lam_t = _get_lambdas(unit)
130
+
131
+ w_u = _safe_numpy(unit.lin_u.weight)
132
+ b_u = _safe_numpy(unit.lin_u.bias)
133
+ w_c = _safe_numpy(unit.lin_c.weight)
134
+ b_c = _safe_numpy(unit.lin_c.bias)
135
+ w_t = _safe_numpy(unit.lin_t.weight)
136
+ b_t = _safe_numpy(unit.lin_t.bias)
137
+
138
+ for j in range(num_basis):
139
+ block_id = j + 1
140
+ print(f"--B{layer_idx}{block_id}")
141
+
142
+ # U part
143
+ lines = _emit_part_lines(
144
+ "U",
145
+ lam_val=float(lam_u[j]),
146
+ w_row=w_u[j],
147
+ b_val=float(b_u[j]),
148
+ feature_names=feature_names,
149
+ ndigits=ndigits,
150
+ tol=tol,
151
+ )
152
+ for ln in lines:
153
+ print(ln)
154
+
155
+ # C part
156
+ lines = _emit_part_lines(
157
+ "C",
158
+ lam_val=float(lam_c[j]),
159
+ w_row=w_c[j],
160
+ b_val=float(b_c[j]),
161
+ feature_names=feature_names,
162
+ ndigits=ndigits,
163
+ tol=tol,
164
+ )
165
+ for ln in lines:
166
+ print(ln)
167
+
168
+ # T part
169
+ lines = _emit_part_lines(
170
+ "T",
171
+ lam_val=float(lam_t[j]),
172
+ w_row=w_t[j],
173
+ b_val=float(b_t[j]),
174
+ feature_names=feature_names,
175
+ ndigits=ndigits,
176
+ tol=tol,
177
+ )
178
+ for ln in lines:
179
+ print(ln)
180
+
181
+ print("")
182
+
183
+ return num_basis
184
+
185
+
186
+ def _print_core(model, blocks, hidden_dims, feat_names, ndigits, tol, title="BL Model Structure"):
187
+ print("=" * 72)
188
+ print(title)
189
+ print("=" * 72)
190
+
191
+ print(f"hidden_dims = {hidden_dims}")
192
+ print(f"feature_dim = {len(feat_names)}")
193
+ print("")
194
+
195
+ current_feat_names = feat_names
196
+
197
+ for layer_idx, block in enumerate(blocks, start=1):
198
+ num_basis = _print_blocks(
199
+ block=block,
200
+ feature_names=current_feat_names,
201
+ layer_idx=layer_idx,
202
+ ndigits=ndigits,
203
+ tol=tol,
204
+ )
205
+
206
+ if layer_idx < len(blocks):
207
+ current_feat_names = [f"B{layer_idx}{i+1}" for i in range(num_basis)]
208
+
209
+ outs = _get_output_linears(model)
210
+ if len(outs) > 0:
211
+ print("=" * 72)
212
+ print("OUTPUT LINEAR(S)")
213
+ print("=" * 72)
214
+ for name, lin in outs.items():
215
+ w = _safe_numpy(lin.weight)
216
+ b = _safe_numpy(lin.bias) if lin.bias is not None else None
217
+ print(name)
218
+ if getattr(w, "size", 0) and w.size <= 16:
219
+ w_flat = [float(x) for x in w.reshape(-1)]
220
+ w_fmt = ", ".join(_fmt_num(x, ndigits) for x in w_flat)
221
+ print(f"weight values = [{w_fmt}]")
222
+
223
+ if b is None:
224
+ print("bias = None")
225
+ else:
226
+ if getattr(b, "size", 0) and b.size <= 16:
227
+ b_flat = [float(x) for x in b.reshape(-1)]
228
+ b_fmt = ", ".join(_fmt_num(x, ndigits) for x in b_flat)
229
+ print(f"bias values = [{b_fmt}]")
230
+ print("")
231
+
232
+ def export_structure(
233
+ model,
234
+ df=None,
235
+ feature_names=None,
236
+ txt_path: Optional[str] = None,
237
+ ndigits: int = 4,
238
+ tol: float = 0.0,
239
+ title: str = "BL Model Structure",
240
+ ):
241
+
242
+ blocks = _get_blocks(model)
243
+ hidden_dims = _get_hidden_dims(model)
244
+
245
+ if feature_names is not None:
246
+ feat_names = list(feature_names)
247
+ elif df is not None:
248
+ feat_names = list(df.columns)
249
+ else:
250
+ unit0 = _get_bl_unit(blocks[0])
251
+ if not hasattr(unit0, "lin_u"):
252
+ raise AttributeError("Cannot infer input dim from BLUnit. Expected 'lin_u'.")
253
+ in_dim = int(unit0.lin_u.in_features)
254
+ feat_names = [f"x{i+1}" for i in range(in_dim)]
255
+
256
+ if txt_path is None:
257
+ _print_core(model, blocks, hidden_dims, feat_names, ndigits, tol, title)
258
+ return
259
+
260
+ old = sys.stdout
261
+ buf = io.StringIO()
262
+ sys.stdout = buf
263
+ try:
264
+ _print_core(model, blocks, hidden_dims, feat_names, ndigits, tol, title)
265
+ finally:
266
+ sys.stdout = old
267
+
268
+ with open(txt_path, "w", encoding="utf-8") as f:
269
+ f.write(buf.getvalue())
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ from .continuous import predict_continuous
4
+ from .discrete import predict_class_discrete, predict_proba_discrete
5
+
6
+ __all__ = [
7
+ "predict_continuous",
8
+ "predict_class_discrete",
9
+ "predict_proba_discrete",
10
+ ]
@@ -0,0 +1,21 @@
1
+ from __future__ import annotations
2
+
3
+ import torch
4
+ import torch.nn as nn
5
+
6
+ from ..training import utils as U
7
+
8
+
9
+ @torch.no_grad()
10
+ def predict_continuous(
11
+ predictor: nn.Module,
12
+ x: torch.Tensor,
13
+ *,
14
+ device: str | torch.device | None = None,
15
+ return_cpu: bool = True,
16
+ ) -> torch.Tensor:
17
+
18
+ predictor.eval()
19
+ dev = U.resolve_device(model=predictor, tensor=x, device=device)
20
+ y_hat = predictor(x.to(dev))
21
+ return y_hat.detach().cpu() if return_cpu else y_hat.detach()
@@ -0,0 +1,40 @@
1
+ import torch
2
+ import torch.nn.functional as F
3
+
4
+ from ..training import utils as U
5
+
6
+
7
+ @torch.no_grad()
8
+ def predict_proba_discrete(
9
+ model,
10
+ x: torch.Tensor,
11
+ *,
12
+ temperature: float = 1.0,
13
+ device: str | torch.device | None = None,
14
+ return_cpu: bool = False,
15
+ ) -> torch.Tensor:
16
+
17
+ dev = U.resolve_device(model, x, device)
18
+ x = x.to(dev)
19
+
20
+ scores = model.logits(x) if hasattr(model, "logits") else model(x)
21
+ logits = scores / float(temperature)
22
+ probs = F.softmax(logits, dim=1)
23
+
24
+ return probs.cpu() if return_cpu else probs
25
+
26
+
27
+ @torch.no_grad()
28
+ def predict_class_discrete(
29
+ model,
30
+ x: torch.Tensor,
31
+ *,
32
+ temperature: float = 1.0,
33
+ device: str | torch.device | None = None,
34
+ return_cpu: bool = True,
35
+ ) -> torch.Tensor:
36
+ probs = predict_proba_discrete(
37
+ model, x, temperature=temperature, device=device, return_cpu=False
38
+ )
39
+ pred = probs.argmax(dim=1)
40
+ return pred.cpu() if return_cpu else pred
@@ -0,0 +1,7 @@
1
+ from __future__ import annotations
2
+
3
+ from .bldeep import BLDeep
4
+
5
+ __all__ = [
6
+ "BLDeep"
7
+ ]