rbp-engine 1.2.4__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
rbp_engine/__init__.py ADDED
@@ -0,0 +1,87 @@
1
+ """rbp_engine — NumPy-friendly Relevance-Based Prediction.
2
+
3
+ Quick start
4
+ -----------
5
+ >>> import numpy as np
6
+ >>> from rbp_engine import predict, PredictOptions, predict_maxfit, predict_grid, GridOptions, relevance
7
+ >>>
8
+ >>> # y: outcomes (N,), X: attributes (N, K), theta: circumstances (K,)
9
+ >>> result = predict(y, X, theta, PredictOptions(threshold=[0.5]))
10
+ >>> result.yhat, result.fit, result.insights.relevance
11
+ >>>
12
+ >>> best = predict_maxfit(y, X, theta) # best threshold for you
13
+ >>> out = predict_grid(y, X, theta, GridOptions(k=2)) # attribute-subset search
14
+ >>> float(out.yhat[0]) # grid composite (T=1)
15
+ >>>
16
+ >>> r = relevance(X, theta) # insights without a full predict
17
+
18
+ Terminology
19
+ -----------
20
+ * ``theta`` always means **circumstances** — the situation you are predicting for.
21
+ * Use ``help(predict)``, ``help(GridOptions)``, etc. for paste-ready examples.
22
+
23
+ See Also
24
+ --------
25
+ predict, predict_maxfit, predict_grid
26
+ PredictOptions, MaxFitOptions, GridOptions
27
+ PredictionResults
28
+ relevance, similarity, info_x, info_theta, relevance_metrics
29
+ """
30
+
31
+ from __future__ import annotations
32
+
33
+ from rbp_engine._version import __version__
34
+ from rbp_engine.config import get_percentile_value_algorithm, set_percentile_value_algorithm
35
+ from rbp_engine.errors import RbpError, RbpLicenseError
36
+ from rbp_engine.insights import (
37
+ RelevanceMetrics,
38
+ info_theta,
39
+ info_x,
40
+ relevance,
41
+ relevance_metrics,
42
+ similarity,
43
+ )
44
+ from rbp_engine.functions import predict, predict_grid, predict_maxfit
45
+ from rbp_engine.options import GridOptions, MaxFitOptions, PredictOptions
46
+ from rbp_engine.results import (
47
+ AuxiliaryInfo,
48
+ CensorCells,
49
+ GridCells,
50
+ GridInsights,
51
+ Insights,
52
+ PredictionResults,
53
+ PredictionWeights,
54
+ SoloDistribution,
55
+ SoloDistributionStatistics,
56
+ YSoloHistogram,
57
+ )
58
+
59
+ __all__ = [
60
+ "__version__",
61
+ "PredictOptions",
62
+ "MaxFitOptions",
63
+ "GridOptions",
64
+ "PredictionResults",
65
+ "AuxiliaryInfo",
66
+ "Insights",
67
+ "PredictionWeights",
68
+ "SoloDistribution",
69
+ "SoloDistributionStatistics",
70
+ "YSoloHistogram",
71
+ "GridInsights",
72
+ "GridCells",
73
+ "CensorCells",
74
+ "RelevanceMetrics",
75
+ "predict",
76
+ "predict_maxfit",
77
+ "predict_grid",
78
+ "relevance",
79
+ "similarity",
80
+ "info_x",
81
+ "info_theta",
82
+ "relevance_metrics",
83
+ "set_percentile_value_algorithm",
84
+ "get_percentile_value_algorithm",
85
+ "RbpError",
86
+ "RbpLicenseError",
87
+ ]
rbp_engine/_arrays.py ADDED
@@ -0,0 +1,38 @@
1
+ """Array helpers: accept NumPy / array-likes, hand contiguous buffers to ctypes."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ctypes import POINTER, c_double
6
+ from typing import Any
7
+
8
+ import numpy as np
9
+ from numpy.typing import ArrayLike, NDArray
10
+
11
+ from rbp_engine._ffi import RBP_LAYOUT_COL_MAJOR, RBP_LAYOUT_ROW_MAJOR
12
+
13
+
14
+ def as_f64_1d(a: ArrayLike, *, name: str) -> NDArray[np.float64]:
15
+ arr = np.asarray(a, dtype=np.float64)
16
+ if arr.ndim == 2 and 1 in arr.shape:
17
+ arr = arr.reshape(-1)
18
+ if arr.ndim != 1:
19
+ raise ValueError(f"{name} must be 1-D (or an N×1 / 1×K column/row); got shape {arr.shape}")
20
+ return np.ascontiguousarray(arr)
21
+
22
+
23
+ def as_f64_2d(a: ArrayLike, *, name: str) -> tuple[NDArray[np.float64], int]:
24
+ """Return (array, layout code). Prefers C-order; uses F-order zero-copy when contiguous."""
25
+ arr = np.asarray(a, dtype=np.float64)
26
+ if arr.ndim != 2:
27
+ raise ValueError(f"{name} must be 2-D; got shape {arr.shape}")
28
+ if arr.flags["C_CONTIGUOUS"]:
29
+ return arr, RBP_LAYOUT_ROW_MAJOR
30
+ if arr.flags["F_CONTIGUOUS"]:
31
+ return arr, RBP_LAYOUT_COL_MAJOR
32
+ return np.ascontiguousarray(arr), RBP_LAYOUT_ROW_MAJOR
33
+
34
+
35
+ def c_double_ptr(arr: NDArray[np.float64]) -> Any:
36
+ if arr.size == 0:
37
+ raise ValueError("array must be non-empty")
38
+ return arr.ctypes.data_as(POINTER(c_double))
rbp_engine/_cli.py ADDED
@@ -0,0 +1,109 @@
1
+ """Console entry point for the bundled ``rbp-license-info`` native CLI.
2
+
3
+ Wheels stage the platform binary at ``rbp_engine/bin/``. The ``[project.scripts]``
4
+ entry ``rbp-license-info`` in ``pyproject.toml`` installs a small launcher on
5
+ PATH that ends up here and replaces itself with that binary (or falls back to a
6
+ cargo ``target/`` build for source checkouts).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import os
12
+ import subprocess
13
+ import sys
14
+ from pathlib import Path
15
+
16
+
17
+ def _cli_names() -> tuple[str, ...]:
18
+ if sys.platform == "win32":
19
+ return ("rbp-license-info.exe", "rbp-license-info")
20
+ return ("rbp-license-info",)
21
+
22
+
23
+ def _cargo_candidates(names: tuple[str, ...]) -> list[Path]:
24
+ # _cli.py → rbp_engine/ → python/ → repo root
25
+ repo_root = Path(__file__).resolve().parents[2]
26
+ paths: list[Path] = []
27
+ for name in names:
28
+ paths.extend(
29
+ [
30
+ repo_root / "target" / "release" / name,
31
+ repo_root / "target" / "debug" / name,
32
+ ]
33
+ )
34
+ cargo_target = os.environ.get("CARGO_TARGET_DIR")
35
+ if cargo_target:
36
+ for name in names:
37
+ paths.extend(
38
+ [
39
+ Path(cargo_target) / "release" / name,
40
+ Path(cargo_target) / "debug" / name,
41
+ ]
42
+ )
43
+ return paths
44
+
45
+
46
+ def find_cli_path() -> Path:
47
+ """Return the path to ``rbp-license-info``, or raise ``FileNotFoundError``."""
48
+ names = _cli_names()
49
+ tried: list[str] = []
50
+
51
+ env = os.environ.get("RBP_LICENSE_INFO")
52
+ if env:
53
+ path = Path(env)
54
+ tried.append(str(path))
55
+ if path.is_file():
56
+ return path.resolve()
57
+ raise FileNotFoundError(f"RBP_LICENSE_INFO={path} is not a file")
58
+
59
+ base = Path(__file__).resolve().parent
60
+ for name in names:
61
+ bundled = base / "bin" / name
62
+ tried.append(str(bundled))
63
+ if bundled.is_file():
64
+ return bundled.resolve()
65
+
66
+ existing: list[Path] = []
67
+ for path in _cargo_candidates(names):
68
+ tried.append(str(path))
69
+ if path.is_file():
70
+ existing.append(path.resolve())
71
+ if existing:
72
+ return max(existing, key=lambda p: p.stat().st_mtime)
73
+
74
+ raise FileNotFoundError(
75
+ "Could not find rbp-license-info. Install a platform wheel of rbp-engine, "
76
+ "build with `cargo build --release --bin rbp-license-info` at the repo "
77
+ "root, or set RBP_LICENSE_INFO to the full path.\n"
78
+ "Tried:\n - " + "\n - ".join(tried)
79
+ )
80
+
81
+
82
+ def _ensure_executable(path: Path) -> None:
83
+ """Restore +x if the wheel unpack stripped permission bits (common for data)."""
84
+ if sys.platform == "win32":
85
+ return
86
+ try:
87
+ mode = path.stat().st_mode
88
+ if mode & 0o111:
89
+ return
90
+ path.chmod(mode | 0o111)
91
+ except OSError:
92
+ pass
93
+
94
+
95
+ def main(argv: list[str] | None = None) -> None:
96
+ """Run the native CLI, replacing this process when possible."""
97
+ args = list(sys.argv[1:] if argv is None else argv)
98
+ path = find_cli_path()
99
+ _ensure_executable(path)
100
+ cmd = [str(path), *args]
101
+ if sys.platform == "win32":
102
+ # os.execv on Windows does not reliably replace the process for
103
+ # console-script launchers; spawn and forward the exit code instead.
104
+ raise SystemExit(subprocess.call(cmd))
105
+ os.execv(str(path), cmd)
106
+
107
+
108
+ if __name__ == "__main__":
109
+ main()
rbp_engine/_ffi.py ADDED
@@ -0,0 +1,460 @@
1
+ """Low-level ctypes bindings. Prefer the public ``rbp_engine`` API."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from ctypes import (
6
+ POINTER,
7
+ c_char_p,
8
+ c_double,
9
+ c_int32,
10
+ c_size_t,
11
+ c_uint32,
12
+ c_uint8,
13
+ c_void_p,
14
+ )
15
+ from typing import Any
16
+
17
+ from rbp_engine._native import load_library
18
+ from rbp_engine.errors import RbpError, RbpLicenseError
19
+
20
+ RBP_OK = 0
21
+ RBP_ERR_LICENSE = 4
22
+ RBP_LAYOUT_ROW_MAJOR = 0
23
+ RBP_LAYOUT_COL_MAJOR = 1
24
+ RBP_GRID_CENSOR_RELEVANCE = 0
25
+ RBP_GRID_CENSOR_SIMILARITY = 1
26
+
27
+ RbpPredictOptionsP = c_void_p
28
+ RbpMaxFitOptionsP = c_void_p
29
+ RbpGridOptionsP = c_void_p
30
+ RbpPredictionResultsP = c_void_p
31
+
32
+
33
+ def _bind_has(lib: Any, name: str, *extra: Any) -> None:
34
+ fn = getattr(lib, name)
35
+ fn.restype = c_int32
36
+ fn.argtypes = [RbpPredictionResultsP, *extra]
37
+
38
+
39
+ def _bind_copy1(lib: Any, name: str) -> None:
40
+ fn = getattr(lib, name)
41
+ fn.restype = c_int32
42
+ fn.argtypes = [RbpPredictionResultsP, POINTER(c_double), c_size_t]
43
+
44
+
45
+ def _bind_dims2(lib: Any, name: str, *extra: Any) -> None:
46
+ fn = getattr(lib, name)
47
+ fn.restype = c_int32
48
+ fn.argtypes = [RbpPredictionResultsP, *extra, POINTER(c_size_t), POINTER(c_size_t)]
49
+
50
+
51
+ def _bind_copy2(lib: Any, name: str, *extra: Any) -> None:
52
+ fn = getattr(lib, name)
53
+ fn.restype = c_int32
54
+ fn.argtypes = [RbpPredictionResultsP, *extra, POINTER(c_double), c_size_t, c_int32]
55
+
56
+
57
+ def _configure_deep_results(lib: Any) -> None:
58
+ for name in (
59
+ "rbp_results_has_weights",
60
+ "rbp_results_has_weights_excluded",
61
+ "rbp_results_has_include",
62
+ "rbp_results_has_auxiliary",
63
+ "rbp_results_has_maxfit_index",
64
+ "rbp_results_has_y_solo",
65
+ "rbp_results_has_xi_solo",
66
+ "rbp_results_has_ysolo_distribution",
67
+ "rbp_results_has_grid_insights",
68
+ "rbp_results_has_grid_cells",
69
+ "rbp_results_has_k_cells",
70
+ "rbp_results_has_combi_cells",
71
+ "rbp_results_has_ysolo_cells",
72
+ ):
73
+ _bind_has(lib, name)
74
+
75
+ for name in (
76
+ "rbp_results_has_yhat_cells",
77
+ "rbp_results_has_adjusted_fit_cells",
78
+ "rbp_results_has_n_cells",
79
+ "rbp_results_has_weights_cells",
80
+ "rbp_results_has_xi_solo_cells",
81
+ ):
82
+ _bind_has(lib, name, c_int32)
83
+
84
+ lib.rbp_results_num_combinations.restype = c_size_t
85
+ lib.rbp_results_num_combinations.argtypes = [RbpPredictionResultsP]
86
+ lib.rbp_results_maxfit_index_len.restype = c_size_t
87
+ lib.rbp_results_maxfit_index_len.argtypes = [RbpPredictionResultsP]
88
+
89
+ for name in (
90
+ "rbp_results_weights_dims",
91
+ "rbp_results_weights_excluded_dims",
92
+ "rbp_results_include_dims",
93
+ "rbp_results_xi_solo_dims",
94
+ "rbp_results_ysolo_distribution_dims",
95
+ "rbp_results_k_cells_dims",
96
+ "rbp_results_combi_cells_dims",
97
+ "rbp_results_ysolo_cells_dims",
98
+ ):
99
+ _bind_dims2(lib, name)
100
+
101
+ for name in (
102
+ "rbp_results_yhat_cells_dims",
103
+ "rbp_results_adjusted_fit_cells_dims",
104
+ "rbp_results_n_cells_dims",
105
+ ):
106
+ _bind_dims2(lib, name, c_int32)
107
+
108
+ for name in (
109
+ "rbp_results_copy_weights",
110
+ "rbp_results_copy_weights_excluded",
111
+ "rbp_results_copy_xi_solo",
112
+ "rbp_results_copy_ysolo_bin_counts",
113
+ "rbp_results_copy_k_cells",
114
+ "rbp_results_copy_combi_cells",
115
+ "rbp_results_copy_ysolo_cells",
116
+ ):
117
+ _bind_copy2(lib, name)
118
+
119
+ for name in (
120
+ "rbp_results_copy_yhat_cells",
121
+ "rbp_results_copy_adjusted_fit_cells",
122
+ "rbp_results_copy_n_cells",
123
+ ):
124
+ _bind_copy2(lib, name, c_int32)
125
+
126
+ lib.rbp_results_copy_include.restype = c_int32
127
+ lib.rbp_results_copy_include.argtypes = [
128
+ RbpPredictionResultsP,
129
+ POINTER(c_uint8),
130
+ c_size_t,
131
+ c_int32,
132
+ ]
133
+
134
+ for name in (
135
+ "rbp_results_copy_aux_phi",
136
+ "rbp_results_copy_aux_lambda_sq",
137
+ "rbp_results_copy_aux_full_var",
138
+ "rbp_results_copy_aux_part_var",
139
+ "rbp_results_copy_aux_r_star",
140
+ "rbp_results_copy_aux_r_star_percent",
141
+ "rbp_results_copy_aux_rho",
142
+ "rbp_results_copy_aux_n",
143
+ "rbp_results_copy_aux_k",
144
+ "rbp_results_copy_weights_concentration",
145
+ "rbp_results_copy_maxfit_index",
146
+ "rbp_results_copy_y_solo",
147
+ "rbp_results_copy_ysolo_sigma",
148
+ "rbp_results_copy_ysolo_skewness",
149
+ "rbp_results_copy_ysolo_kurtosis",
150
+ "rbp_results_copy_ysolo_pearson_modality_index",
151
+ "rbp_results_copy_ysolo_bimodal_index",
152
+ "rbp_results_copy_ysolo_bin_edges",
153
+ "rbp_results_copy_ysolo_bin_centers",
154
+ "rbp_results_copy_ysolo_bin_widths",
155
+ "rbp_results_copy_variable_weights",
156
+ "rbp_results_copy_mctc",
157
+ "rbp_results_copy_mctp",
158
+ "rbp_results_copy_cctp",
159
+ "rbp_results_copy_xi_solo_composite",
160
+ ):
161
+ _bind_copy1(lib, name)
162
+
163
+ for name in ("rbp_results_weights_cells_dims", "rbp_results_xi_solo_cells_dims"):
164
+ fn = getattr(lib, name)
165
+ fn.restype = c_int32
166
+ fn.argtypes = [
167
+ RbpPredictionResultsP,
168
+ c_int32,
169
+ POINTER(c_size_t),
170
+ POINTER(c_size_t),
171
+ POINTER(c_size_t),
172
+ ]
173
+
174
+ for name in ("rbp_results_copy_weights_cells", "rbp_results_copy_xi_solo_cells"):
175
+ fn = getattr(lib, name)
176
+ fn.restype = c_int32
177
+ fn.argtypes = [RbpPredictionResultsP, c_int32, POINTER(c_double), c_size_t]
178
+
179
+
180
+ def _configure(lib: Any) -> Any:
181
+ lib.rbp_abi_version.restype = c_uint32
182
+ lib.rbp_abi_version.argtypes = []
183
+
184
+ lib.rbp_last_error.restype = c_char_p
185
+ lib.rbp_last_error.argtypes = []
186
+
187
+ lib.rbp_set_percentile_value_algorithm.restype = c_int32
188
+ lib.rbp_set_percentile_value_algorithm.argtypes = [c_int32]
189
+ lib.rbp_get_percentile_value_algorithm.restype = c_int32
190
+ lib.rbp_get_percentile_value_algorithm.argtypes = []
191
+ lib.rbp_set_percentile_value_algorithm_str.restype = c_int32
192
+ lib.rbp_set_percentile_value_algorithm_str.argtypes = [c_char_p]
193
+
194
+ # Predict options
195
+ lib.rbp_predict_options_create.restype = RbpPredictOptionsP
196
+ lib.rbp_predict_options_create.argtypes = []
197
+ lib.rbp_predict_options_free.argtypes = [RbpPredictOptionsP]
198
+
199
+ lib.rbp_predict_options_set_threshold.restype = c_int32
200
+ lib.rbp_predict_options_set_threshold.argtypes = [
201
+ RbpPredictOptionsP,
202
+ POINTER(c_double),
203
+ c_size_t,
204
+ ]
205
+ for name in (
206
+ "rbp_predict_options_set_censor_type",
207
+ "rbp_predict_options_set_censor_unit",
208
+ "rbp_predict_options_set_censor_operator",
209
+ "rbp_predict_options_set_prediction_scale",
210
+ "rbp_predict_options_set_adj_fit_multiplier",
211
+ "rbp_predict_options_set_inv_method",
212
+ "rbp_predict_options_set_verify_missing_data",
213
+ "rbp_predict_options_set_include_linear_regression",
214
+ ):
215
+ fn = getattr(lib, name)
216
+ fn.restype = c_int32
217
+ fn.argtypes = [RbpPredictOptionsP, c_int32]
218
+
219
+ for name in (
220
+ "rbp_predict_options_set_censor_type_str",
221
+ "rbp_predict_options_set_censor_unit_str",
222
+ "rbp_predict_options_set_censor_operator_str",
223
+ ):
224
+ fn = getattr(lib, name)
225
+ fn.restype = c_int32
226
+ fn.argtypes = [RbpPredictOptionsP, c_char_p]
227
+
228
+ lib.rbp_predict.restype = c_int32
229
+ lib.rbp_predict.argtypes = [
230
+ POINTER(c_double),
231
+ c_size_t,
232
+ POINTER(c_double),
233
+ c_size_t,
234
+ c_size_t,
235
+ c_int32,
236
+ POINTER(c_double),
237
+ c_size_t,
238
+ RbpPredictOptionsP,
239
+ POINTER(RbpPredictionResultsP),
240
+ ]
241
+
242
+ # MaxFit
243
+ lib.rbp_maxfit_options_create.restype = RbpMaxFitOptionsP
244
+ lib.rbp_maxfit_options_create.argtypes = []
245
+ lib.rbp_maxfit_options_free.argtypes = [RbpMaxFitOptionsP]
246
+ lib.rbp_maxfit_options_set_objective.restype = c_int32
247
+ lib.rbp_maxfit_options_set_objective.argtypes = [RbpMaxFitOptionsP, c_char_p]
248
+ lib.rbp_maxfit_options_set_threshold.restype = c_int32
249
+ lib.rbp_maxfit_options_set_threshold.argtypes = [
250
+ RbpMaxFitOptionsP,
251
+ POINTER(c_double),
252
+ c_size_t,
253
+ ]
254
+ for name in (
255
+ "rbp_maxfit_options_set_censor_type",
256
+ "rbp_maxfit_options_set_censor_unit",
257
+ "rbp_maxfit_options_set_censor_operator",
258
+ "rbp_maxfit_options_set_prediction_scale",
259
+ "rbp_maxfit_options_set_adj_fit_multiplier",
260
+ "rbp_maxfit_options_set_inv_method",
261
+ "rbp_maxfit_options_set_verify_missing_data",
262
+ "rbp_maxfit_options_set_include_linear_regression",
263
+ "rbp_maxfit_options_set_inner_parallel",
264
+ ):
265
+ fn = getattr(lib, name)
266
+ fn.restype = c_int32
267
+ fn.argtypes = [RbpMaxFitOptionsP, c_int32]
268
+ lib.rbp_maxfit_options_set_censor_type_str.restype = c_int32
269
+ lib.rbp_maxfit_options_set_censor_type_str.argtypes = [RbpMaxFitOptionsP, c_char_p]
270
+ lib.rbp_maxfit_options_set_inner_parallel_str.restype = c_int32
271
+ lib.rbp_maxfit_options_set_inner_parallel_str.argtypes = [RbpMaxFitOptionsP, c_char_p]
272
+
273
+ lib.rbp_maxfit.restype = c_int32
274
+ lib.rbp_maxfit.argtypes = [
275
+ POINTER(c_double),
276
+ c_size_t,
277
+ POINTER(c_double),
278
+ c_size_t,
279
+ c_size_t,
280
+ c_int32,
281
+ POINTER(c_double),
282
+ c_size_t,
283
+ RbpMaxFitOptionsP,
284
+ POINTER(RbpPredictionResultsP),
285
+ ]
286
+
287
+ # Grid
288
+ lib.rbp_grid_options_create.restype = RbpGridOptionsP
289
+ lib.rbp_grid_options_create.argtypes = []
290
+ lib.rbp_grid_options_free.argtypes = [RbpGridOptionsP]
291
+ lib.rbp_grid_options_set_max_iter.restype = c_int32
292
+ lib.rbp_grid_options_set_max_iter.argtypes = [RbpGridOptionsP, c_size_t]
293
+ lib.rbp_grid_options_set_k.restype = c_int32
294
+ lib.rbp_grid_options_set_k.argtypes = [RbpGridOptionsP, c_size_t]
295
+ lib.rbp_grid_options_set_seed.restype = c_int32
296
+ lib.rbp_grid_options_set_seed.argtypes = [RbpGridOptionsP, c_uint32]
297
+ lib.rbp_grid_options_set_retain_all.restype = c_int32
298
+ lib.rbp_grid_options_set_retain_all.argtypes = [RbpGridOptionsP, c_int32]
299
+ lib.rbp_grid_options_set_retain_grid_objects_str.restype = c_int32
300
+ lib.rbp_grid_options_set_retain_grid_objects_str.argtypes = [RbpGridOptionsP, c_char_p]
301
+ lib.rbp_grid_options_set_attribute_combi.restype = c_int32
302
+ lib.rbp_grid_options_set_attribute_combi.argtypes = [
303
+ RbpGridOptionsP,
304
+ POINTER(c_double),
305
+ c_size_t,
306
+ c_size_t,
307
+ c_int32,
308
+ ]
309
+ lib.rbp_grid_options_set_threshold.restype = c_int32
310
+ lib.rbp_grid_options_set_threshold.argtypes = [
311
+ RbpGridOptionsP,
312
+ POINTER(c_double),
313
+ c_size_t,
314
+ ]
315
+ for name in (
316
+ "rbp_grid_options_set_censor_type",
317
+ "rbp_grid_options_set_censor_unit",
318
+ "rbp_grid_options_set_censor_operator",
319
+ "rbp_grid_options_set_prediction_scale",
320
+ "rbp_grid_options_set_adj_fit_multiplier",
321
+ "rbp_grid_options_set_inv_method",
322
+ "rbp_grid_options_set_verify_missing_data",
323
+ "rbp_grid_options_set_include_linear_regression",
324
+ "rbp_grid_options_set_inner_parallel",
325
+ ):
326
+ fn = getattr(lib, name)
327
+ fn.restype = c_int32
328
+ fn.argtypes = [RbpGridOptionsP, c_int32]
329
+ lib.rbp_grid_options_set_censor_type_str.restype = c_int32
330
+ lib.rbp_grid_options_set_censor_type_str.argtypes = [RbpGridOptionsP, c_char_p]
331
+ lib.rbp_grid_options_set_inner_parallel_str.restype = c_int32
332
+ lib.rbp_grid_options_set_inner_parallel_str.argtypes = [RbpGridOptionsP, c_char_p]
333
+
334
+ lib.rbp_grid.restype = c_int32
335
+ lib.rbp_grid.argtypes = [
336
+ POINTER(c_double),
337
+ c_size_t,
338
+ POINTER(c_double),
339
+ c_size_t,
340
+ c_size_t,
341
+ c_int32,
342
+ POINTER(c_double),
343
+ c_size_t,
344
+ RbpGridOptionsP,
345
+ POINTER(RbpPredictionResultsP),
346
+ ]
347
+
348
+ # Insights
349
+ for name in ("rbp_relevance", "rbp_similarity", "rbp_info_theta"):
350
+ fn = getattr(lib, name)
351
+ fn.restype = c_int32
352
+ fn.argtypes = [
353
+ POINTER(c_double),
354
+ c_size_t,
355
+ c_size_t,
356
+ c_int32,
357
+ POINTER(c_double),
358
+ c_size_t,
359
+ POINTER(c_double),
360
+ c_int32,
361
+ POINTER(c_double),
362
+ c_size_t,
363
+ ]
364
+
365
+ lib.rbp_info_x.restype = c_int32
366
+ lib.rbp_info_x.argtypes = [
367
+ POINTER(c_double),
368
+ c_size_t,
369
+ c_size_t,
370
+ c_int32,
371
+ POINTER(c_double),
372
+ c_int32,
373
+ POINTER(c_double),
374
+ c_size_t,
375
+ ]
376
+
377
+ lib.rbp_relevance_metrics.restype = c_int32
378
+ lib.rbp_relevance_metrics.argtypes = [
379
+ POINTER(c_double),
380
+ c_size_t,
381
+ c_size_t,
382
+ c_int32,
383
+ POINTER(c_double),
384
+ c_size_t,
385
+ POINTER(c_double),
386
+ c_int32,
387
+ POINTER(c_double),
388
+ c_size_t,
389
+ POINTER(c_double),
390
+ c_size_t,
391
+ POINTER(c_double),
392
+ c_size_t,
393
+ POINTER(c_double),
394
+ c_size_t,
395
+ ]
396
+
397
+ # Results
398
+ lib.rbp_prediction_results_free.argtypes = [RbpPredictionResultsP]
399
+ for name in (
400
+ "rbp_results_num_observations",
401
+ "rbp_results_num_variables",
402
+ "rbp_results_num_thresholds",
403
+ ):
404
+ fn = getattr(lib, name)
405
+ fn.restype = c_size_t
406
+ fn.argtypes = [RbpPredictionResultsP]
407
+
408
+ for name in (
409
+ "rbp_results_copy_thresholds",
410
+ "rbp_results_copy_yhat",
411
+ "rbp_results_copy_fit",
412
+ "rbp_results_copy_adjusted_fit",
413
+ "rbp_results_copy_agreement",
414
+ "rbp_results_copy_asymmetry",
415
+ "rbp_results_copy_k_fit",
416
+ "rbp_results_copy_outlier_influence",
417
+ "rbp_results_copy_yhat_linear",
418
+ "rbp_results_copy_relevance",
419
+ "rbp_results_copy_similarity",
420
+ "rbp_results_copy_info_x",
421
+ "rbp_results_copy_info_theta",
422
+ ):
423
+ fn = getattr(lib, name)
424
+ fn.restype = c_int32
425
+ fn.argtypes = [RbpPredictionResultsP, POINTER(c_double), c_size_t]
426
+
427
+ lib.rbp_results_has_yhat_linear.restype = c_int32
428
+ lib.rbp_results_has_yhat_linear.argtypes = [RbpPredictionResultsP]
429
+
430
+ _configure_deep_results(lib)
431
+ return lib
432
+
433
+
434
+ _configured: Any | None = None
435
+
436
+
437
+ def get_lib() -> Any:
438
+ global _configured
439
+ if _configured is None:
440
+ # Clear native CDLL cache if ABI symbols were added since last load in-process.
441
+ load_library.cache_clear()
442
+ _configured = _configure(load_library())
443
+ return _configured
444
+
445
+
446
+ def last_error(lib: Any | None = None) -> str:
447
+ lib = lib or get_lib()
448
+ msg = lib.rbp_last_error()
449
+ if not msg:
450
+ return ""
451
+ return msg.decode("utf-8", errors="replace")
452
+
453
+
454
+ def check(status: int, lib: Any | None = None) -> None:
455
+ if status == RBP_OK:
456
+ return
457
+ message = last_error(lib) or f"RBP native call failed (status={status})"
458
+ if status == RBP_ERR_LICENSE:
459
+ raise RbpLicenseError(message, status=status)
460
+ raise RbpError(message, status=status)