UQPyL 2.1.7__cp314-cp314-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.
Files changed (212) hide show
  1. UQPyL/__init__.py +37 -0
  2. UQPyL/analysis/__init__.py +18 -0
  3. UQPyL/analysis/base.py +264 -0
  4. UQPyL/analysis/methods/__init__.py +23 -0
  5. UQPyL/analysis/methods/_variance.py +85 -0
  6. UQPyL/analysis/methods/delta.py +440 -0
  7. UQPyL/analysis/methods/fast.py +159 -0
  8. UQPyL/analysis/methods/mars.py +236 -0
  9. UQPyL/analysis/methods/morris.py +203 -0
  10. UQPyL/analysis/methods/rbd_fast.py +146 -0
  11. UQPyL/analysis/methods/rsa.py +199 -0
  12. UQPyL/analysis/methods/sobol.py +378 -0
  13. UQPyL/analysis/runtime/__init__.py +15 -0
  14. UQPyL/analysis/runtime/reader.py +113 -0
  15. UQPyL/analysis/runtime/result.py +153 -0
  16. UQPyL/analysis/runtime/storage.py +132 -0
  17. UQPyL/analysis/runtime/verbose.py +159 -0
  18. UQPyL/calibration/__init__.py +16 -0
  19. UQPyL/calibration/base.py +209 -0
  20. UQPyL/calibration/methods/__init__.py +11 -0
  21. UQPyL/calibration/methods/_ensemble.py +130 -0
  22. UQPyL/calibration/methods/_uncertainty.py +29 -0
  23. UQPyL/calibration/methods/es.py +152 -0
  24. UQPyL/calibration/methods/glue.py +107 -0
  25. UQPyL/calibration/methods/ies.py +297 -0
  26. UQPyL/calibration/methods/sufi2.py +330 -0
  27. UQPyL/calibration/reader.py +86 -0
  28. UQPyL/calibration/runtime.py +337 -0
  29. UQPyL/calibration/util.py +238 -0
  30. UQPyL/core/__init__.py +26 -0
  31. UQPyL/core/config.py +51 -0
  32. UQPyL/core/parameter_store.py +30 -0
  33. UQPyL/core/params.py +19 -0
  34. UQPyL/core/runtime.py +95 -0
  35. UQPyL/core/runtime_lifecycle.py +73 -0
  36. UQPyL/core/runtime_reader.py +82 -0
  37. UQPyL/core/runtime_session.py +22 -0
  38. UQPyL/core/runtime_storage.py +109 -0
  39. UQPyL/doe/__init__.py +13 -0
  40. UQPyL/doe/base.py +163 -0
  41. UQPyL/doe/methods/__init__.py +17 -0
  42. UQPyL/doe/methods/_sobol.py +26 -0
  43. UQPyL/doe/methods/fast.py +140 -0
  44. UQPyL/doe/methods/full_fact.py +111 -0
  45. UQPyL/doe/methods/lhs.py +266 -0
  46. UQPyL/doe/methods/morris.py +137 -0
  47. UQPyL/doe/methods/random.py +50 -0
  48. UQPyL/doe/methods/saltelli.py +152 -0
  49. UQPyL/doe/methods/sobol.py +99 -0
  50. UQPyL/inference/__init__.py +16 -0
  51. UQPyL/inference/base.py +475 -0
  52. UQPyL/inference/chain.py +40 -0
  53. UQPyL/inference/diagnostics.py +159 -0
  54. UQPyL/inference/methods/__init__.py +13 -0
  55. UQPyL/inference/methods/amh.py +228 -0
  56. UQPyL/inference/methods/demc.py +143 -0
  57. UQPyL/inference/methods/dream_zs.py +359 -0
  58. UQPyL/inference/methods/mh.py +174 -0
  59. UQPyL/inference/methods/mh_gibbs.py +183 -0
  60. UQPyL/inference/runtime/__init__.py +14 -0
  61. UQPyL/inference/runtime/reader.py +135 -0
  62. UQPyL/inference/runtime/result.py +336 -0
  63. UQPyL/inference/runtime/storage.py +199 -0
  64. UQPyL/inference/runtime/verbose.py +347 -0
  65. UQPyL/optimization/__init__.py +20 -0
  66. UQPyL/optimization/base.py +405 -0
  67. UQPyL/optimization/core/__init__.py +6 -0
  68. UQPyL/optimization/core/constraint.py +124 -0
  69. UQPyL/optimization/core/crowding_distance.py +57 -0
  70. UQPyL/optimization/core/ga_operator.py +114 -0
  71. UQPyL/optimization/core/non_dominated_sort.py +66 -0
  72. UQPyL/optimization/core/numerical.py +52 -0
  73. UQPyL/optimization/core/tournament.py +24 -0
  74. UQPyL/optimization/core/uniform_point.py +64 -0
  75. UQPyL/optimization/expensive/__init__.py +9 -0
  76. UQPyL/optimization/expensive/_base.py +74 -0
  77. UQPyL/optimization/expensive/asmo.py +154 -0
  78. UQPyL/optimization/expensive/ego.py +158 -0
  79. UQPyL/optimization/expensive/moasmo.py +235 -0
  80. UQPyL/optimization/metric/__init__.py +3 -0
  81. UQPyL/optimization/metric/_distance.py +49 -0
  82. UQPyL/optimization/metric/gd.py +5 -0
  83. UQPyL/optimization/metric/hv.py +198 -0
  84. UQPyL/optimization/metric/igd.py +5 -0
  85. UQPyL/optimization/moea/__init__.py +5 -0
  86. UQPyL/optimization/moea/moea_d.py +202 -0
  87. UQPyL/optimization/moea/nsga_ii.py +193 -0
  88. UQPyL/optimization/moea/nsga_iii.py +279 -0
  89. UQPyL/optimization/moea/rvea.py +240 -0
  90. UQPyL/optimization/population.py +212 -0
  91. UQPyL/optimization/runtime/__init__.py +4 -0
  92. UQPyL/optimization/runtime/reader.py +321 -0
  93. UQPyL/optimization/runtime/result.py +448 -0
  94. UQPyL/optimization/runtime/storage.py +259 -0
  95. UQPyL/optimization/runtime/verbose.py +406 -0
  96. UQPyL/optimization/soea/__init__.py +8 -0
  97. UQPyL/optimization/soea/abc.py +328 -0
  98. UQPyL/optimization/soea/csa.py +234 -0
  99. UQPyL/optimization/soea/de.py +156 -0
  100. UQPyL/optimization/soea/ga.py +142 -0
  101. UQPyL/optimization/soea/ml_sce_ua.py +219 -0
  102. UQPyL/optimization/soea/pso.py +203 -0
  103. UQPyL/optimization/soea/sce_ua.py +210 -0
  104. UQPyL/problem/__init__.py +73 -0
  105. UQPyL/problem/base.py +241 -0
  106. UQPyL/problem/decorators.py +26 -0
  107. UQPyL/problem/eval.py +38 -0
  108. UQPyL/problem/evaluator.py +19 -0
  109. UQPyL/problem/evaluator_base.py +29 -0
  110. UQPyL/problem/model_evaluator.py +21 -0
  111. UQPyL/problem/model_problem.py +254 -0
  112. UQPyL/problem/mop/DTLZ.py +724 -0
  113. UQPyL/problem/mop/ZDT.py +233 -0
  114. UQPyL/problem/mop/__init__.py +2 -0
  115. UQPyL/problem/problem.py +148 -0
  116. UQPyL/problem/simulator_base.py +7 -0
  117. UQPyL/problem/sop/__init__.py +19 -0
  118. UQPyL/problem/sop/single_constraint_problem.py +52 -0
  119. UQPyL/problem/sop/single_simple_problem.py +564 -0
  120. UQPyL/problem/space.py +220 -0
  121. UQPyL/surrogate/__init__.py +24 -0
  122. UQPyL/surrogate/_kernel.py +149 -0
  123. UQPyL/surrogate/_numeric.py +25 -0
  124. UQPyL/surrogate/_restart.py +40 -0
  125. UQPyL/surrogate/auto_tuner.py +552 -0
  126. UQPyL/surrogate/base.py +467 -0
  127. UQPyL/surrogate/gp/__init__.py +2 -0
  128. UQPyL/surrogate/gp/gaussian_process.py +268 -0
  129. UQPyL/surrogate/gp/kernel/__init__.py +7 -0
  130. UQPyL/surrogate/gp/kernel/base_kernel.py +61 -0
  131. UQPyL/surrogate/gp/kernel/c_kernel_.py +30 -0
  132. UQPyL/surrogate/gp/kernel/dot_kernel_.py +33 -0
  133. UQPyL/surrogate/gp/kernel/matern_kernel.py +167 -0
  134. UQPyL/surrogate/gp/kernel/rbf_kernel.py +44 -0
  135. UQPyL/surrogate/gp/kernel/rq_kernel.py +55 -0
  136. UQPyL/surrogate/kriging/__init__.py +2 -0
  137. UQPyL/surrogate/kriging/kernel/__init__.py +4 -0
  138. UQPyL/surrogate/kriging/kernel/base_kernel.py +36 -0
  139. UQPyL/surrogate/kriging/kernel/cubic_kernel.py +33 -0
  140. UQPyL/surrogate/kriging/kernel/exp_kernel.py +32 -0
  141. UQPyL/surrogate/kriging/kernel/guass_kernel.py +32 -0
  142. UQPyL/surrogate/kriging/kriging.py +326 -0
  143. UQPyL/surrogate/mars/__init__.py +1 -0
  144. UQPyL/surrogate/mars/core/__init__.py +4 -0
  145. UQPyL/surrogate/mars/core/_basis.cp314-win_amd64.pyd +0 -0
  146. UQPyL/surrogate/mars/core/_basis.pxd +239 -0
  147. UQPyL/surrogate/mars/core/_basis.pyx +1120 -0
  148. UQPyL/surrogate/mars/core/_forward.cp314-win_amd64.pyd +0 -0
  149. UQPyL/surrogate/mars/core/_forward.pxd +93 -0
  150. UQPyL/surrogate/mars/core/_forward.pyx +579 -0
  151. UQPyL/surrogate/mars/core/_knot_search.cp314-win_amd64.pyd +0 -0
  152. UQPyL/surrogate/mars/core/_knot_search.pxd +94 -0
  153. UQPyL/surrogate/mars/core/_knot_search.pyx +678 -0
  154. UQPyL/surrogate/mars/core/_pruning.cp314-win_amd64.pyd +0 -0
  155. UQPyL/surrogate/mars/core/_pruning.pxd +25 -0
  156. UQPyL/surrogate/mars/core/_pruning.pyx +215 -0
  157. UQPyL/surrogate/mars/core/_qr.cp314-win_amd64.pyd +0 -0
  158. UQPyL/surrogate/mars/core/_qr.pxd +34 -0
  159. UQPyL/surrogate/mars/core/_qr.pyx +264 -0
  160. UQPyL/surrogate/mars/core/_record.cp314-win_amd64.pyd +0 -0
  161. UQPyL/surrogate/mars/core/_record.pxd +66 -0
  162. UQPyL/surrogate/mars/core/_record.pyx +332 -0
  163. UQPyL/surrogate/mars/core/_types.cp314-win_amd64.pyd +0 -0
  164. UQPyL/surrogate/mars/core/_types.pxd +5 -0
  165. UQPyL/surrogate/mars/core/_types.pyx +5 -0
  166. UQPyL/surrogate/mars/core/_util.cp314-win_amd64.pyd +0 -0
  167. UQPyL/surrogate/mars/core/_util.pxd +23 -0
  168. UQPyL/surrogate/mars/core/_util.pyx +94 -0
  169. UQPyL/surrogate/mars/mars.py +804 -0
  170. UQPyL/surrogate/metric.py +74 -0
  171. UQPyL/surrogate/poly.py +62 -0
  172. UQPyL/surrogate/rbf/__init__.py +2 -0
  173. UQPyL/surrogate/rbf/kernel/__init__.py +8 -0
  174. UQPyL/surrogate/rbf/kernel/base_kernel.py +75 -0
  175. UQPyL/surrogate/rbf/kernel/cubic_kernel.py +33 -0
  176. UQPyL/surrogate/rbf/kernel/gaussian_kernel.py +21 -0
  177. UQPyL/surrogate/rbf/kernel/linear_kernel.py +30 -0
  178. UQPyL/surrogate/rbf/kernel/multiquadric_kernel.py +31 -0
  179. UQPyL/surrogate/rbf/kernel/thin_plate_spline_kernel.py +37 -0
  180. UQPyL/surrogate/rbf/radial_basis_function.py +197 -0
  181. UQPyL/surrogate/regression/__init__.py +2 -0
  182. UQPyL/surrogate/regression/lasso/__init__.py +8 -0
  183. UQPyL/surrogate/regression/lasso/cython_utils.pxd +54 -0
  184. UQPyL/surrogate/regression/lasso/cython_utils.pyx +472 -0
  185. UQPyL/surrogate/regression/lasso/lasso.cp314-win_amd64.pyd +0 -0
  186. UQPyL/surrogate/regression/lasso/lasso_fast.pyx +852 -0
  187. UQPyL/surrogate/regression/linear_regression.py +275 -0
  188. UQPyL/surrogate/regression/polynomial_regression.py +151 -0
  189. UQPyL/surrogate/scaler.py +178 -0
  190. UQPyL/surrogate/setting.py +376 -0
  191. UQPyL/surrogate/split.py +57 -0
  192. UQPyL/surrogate/svr/__init__.py +1 -0
  193. UQPyL/surrogate/svr/core/__init__.py +7 -0
  194. UQPyL/surrogate/svr/core/libsvm_interface.cp314-win_amd64.pyd +0 -0
  195. UQPyL/surrogate/svr/core/libsvm_interface.cpp +147 -0
  196. UQPyL/surrogate/svr/core/svm.cpp +3329 -0
  197. UQPyL/surrogate/svr/core/svm.h +108 -0
  198. UQPyL/surrogate/svr/support_vector_machine.py +240 -0
  199. UQPyL/surrogate/util/__init__.py +0 -0
  200. UQPyL/surrogate/util/boxmin.py +147 -0
  201. UQPyL/surrogate/util/lbfgsb.py +67 -0
  202. UQPyL/viz/__init__.py +23 -0
  203. UQPyL/viz/analysis.py +91 -0
  204. UQPyL/viz/common.py +29 -0
  205. UQPyL/viz/inference.py +184 -0
  206. UQPyL/viz/optimization.py +319 -0
  207. UQPyL/viz/surrogate.py +68 -0
  208. uqpyl-2.1.7.dist-info/METADATA +346 -0
  209. uqpyl-2.1.7.dist-info/RECORD +212 -0
  210. uqpyl-2.1.7.dist-info/WHEEL +5 -0
  211. uqpyl-2.1.7.dist-info/licenses/LICENSE.md +21 -0
  212. uqpyl-2.1.7.dist-info/top_level.txt +1 -0
UQPyL/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ """UQPyL package entry."""
2
+
3
+ import importlib
4
+ from types import ModuleType
5
+
6
+ from .core import config
7
+
8
+ doe = importlib.import_module(__name__ + ".doe")
9
+
10
+ problem = importlib.import_module(__name__ + ".problem")
11
+ viz = importlib.import_module(__name__ + ".viz")
12
+
13
+
14
+ def _import_submodule(name: str) -> ModuleType:
15
+ return importlib.import_module(f"{__name__}.{name}")
16
+
17
+
18
+ surrogate = _import_submodule("surrogate")
19
+ optimization = _import_submodule("optimization")
20
+ analysis = _import_submodule("analysis")
21
+ inference = _import_submodule("inference")
22
+ calibration = _import_submodule("calibration")
23
+
24
+ __version__ = "2.1.7"
25
+ __author__ = "wmtSky"
26
+
27
+ __all__ = [
28
+ "problem",
29
+ "surrogate",
30
+ "optimization",
31
+ "analysis",
32
+ "doe",
33
+ "inference",
34
+ "calibration",
35
+ "viz",
36
+ "config",
37
+ ]
@@ -0,0 +1,18 @@
1
+ from .methods.delta import DeltaTest
2
+ from .methods.fast import FAST
3
+ from .methods.morris import Morris
4
+ from .methods.rbd_fast import RBDFAST
5
+ from .methods.rsa import RSA
6
+ from .methods.sobol import Sobol
7
+
8
+ from .methods import MARS
9
+
10
+ __all__ = [
11
+ "DeltaTest",
12
+ "FAST",
13
+ "MARS",
14
+ "Morris",
15
+ "RBDFAST",
16
+ "RSA",
17
+ "Sobol",
18
+ ]
UQPyL/analysis/base.py ADDED
@@ -0,0 +1,264 @@
1
+ import abc
2
+ import time
3
+ from inspect import signature
4
+ from typing import List, Union
5
+
6
+ import numpy as np
7
+
8
+ from ..core.params import Params
9
+ from ..core import config
10
+ from ..core.runtime_session import RunSession
11
+ from ..core.runtime_lifecycle import RunLifecycle
12
+ from ..problem import ProblemABC as Problem
13
+ from .runtime import AnaState, SqliteStorage, Verbose
14
+
15
+ AnaIndex = Union[str, int, List[int]]
16
+
17
+
18
+ class AnalysisABC(RunLifecycle, metaclass=abc.ABCMeta):
19
+ """
20
+ Abstract base class for analysis methods.
21
+ Shared workflow and utilities for sensitivity analysis methods.
22
+ """
23
+
24
+ _runEntryPoint = "analyze"
25
+
26
+ def __init__(self, verboseFlag: bool = False, logFlag: bool = False, saveFlag: bool = False):
27
+ """
28
+ Initialize the analysis base class.
29
+
30
+ Args:
31
+ verboseFlag: Whether to print compact runtime summaries.
32
+ logFlag: Whether to write a log file.
33
+ saveFlag: Whether to persist results to sqlite.
34
+ """
35
+
36
+ # Set flags for verbosity, logging, and saving
37
+ self.verboseFlag = verboseFlag
38
+ self.logFlag = logFlag
39
+ self.saveFlag = saveFlag
40
+
41
+ # Initialize settings and results
42
+ self.params = Params()
43
+ self.state = AnaState(self)
44
+ self.storage = None
45
+ self.session: RunSession | None = None
46
+ self.runId = None
47
+
48
+ def set(self, key, value):
49
+ """
50
+ Set an analysis parameter.
51
+
52
+ Args:
53
+ key: Parameter name.
54
+ value: Parameter value.
55
+ """
56
+
57
+ self.params.set(key, value)
58
+
59
+ def get(self, *args):
60
+ """
61
+ Retrieve one or more analysis parameters.
62
+
63
+ Args:
64
+ *args: Parameter names.
65
+
66
+ Returns:
67
+ The requested parameter value or values.
68
+ """
69
+
70
+ return self.params.get(*args)
71
+
72
+ def setProblem(self, problem: Problem):
73
+ """
74
+ Set the problem instance for the analysis.
75
+
76
+ Args:
77
+ problem: Problem instance defining the input and output space.
78
+ """
79
+
80
+ self.problem = problem
81
+
82
+ def setup(self, problem):
83
+ self.setProblem(problem)
84
+ self._startRun()
85
+ self.state.reset()
86
+ self.session = None
87
+ Verbose.setupContext(self, problem)
88
+ if self.saveFlag:
89
+ rootDir = config.resolveWorkDir(getattr(problem, "workDir", None), Verbose.workDir)
90
+ self.storage = SqliteStorage(rootDir)
91
+ self.session = self.storage.create_run(self)
92
+ self.runId = self.session.run_id
93
+
94
+ def finalize(self):
95
+ result = self.state.buildResult()
96
+ if self.saveFlag and self.session is not None:
97
+ self.storage.saveResult(self.session, result)
98
+ self._closeStandaloneSession()
99
+ Verbose.printConclusion(self, result)
100
+ if self.logFlag:
101
+ Verbose.saveLog(self)
102
+ return result
103
+
104
+ def analyze(self, problem, *args, **kwargs):
105
+ """
106
+ Run the analysis workflow and return the final `AnaResult`.
107
+
108
+ Expected public inputs follow the unified protocol:
109
+ `analyze(problem, X, Y=None, meta=None, target="objs", index="all")`.
110
+ Here `target` is the semantic label of `Y`, and when `Y` is not
111
+ provided it also selects which problem output block to evaluate.
112
+ Metadata with output="unit" decodes X before evaluation and analysis;
113
+ supplied Y must describe those same decoded samples. Results store
114
+ real X and output="real", retaining source_output="unit" as provenance.
115
+ """
116
+ call = signature(self._analyzeCore).bind(problem, *args, **kwargs)
117
+ meta = call.arguments.get("meta")
118
+ if meta is not None:
119
+ if not isinstance(meta, dict):
120
+ raise TypeError("Sampling metadata must be a dict.")
121
+ output = meta.get("output", "real")
122
+ if output not in ("real", "unit"):
123
+ raise ValueError("Sampling metadata output must be 'real' or 'unit'.")
124
+ meta = dict(meta)
125
+ if output == "unit":
126
+ call.arguments["X"] = problem.unit_to_space(call.arguments["X"])
127
+ meta["source_output"] = "unit"
128
+ meta["output"] = "real"
129
+ call.arguments["meta"] = meta
130
+ self.checkMeta(meta)
131
+ self.setup(problem)
132
+ Verbose.printSettings(self)
133
+ start = time.perf_counter()
134
+ self._analyzeCore(*call.args, **call.kwargs)
135
+ self.state.runtime = time.perf_counter() - start
136
+ return self.finalize()
137
+
138
+ def checkMeta(self, meta):
139
+ """
140
+ Validate sampling metadata produced by `sampleWithMeta()`.
141
+ """
142
+ return None
143
+
144
+ def check_Y(self, X, Y, target: str = "objs", index: AnaIndex = "all"):
145
+ """
146
+ Resolve and slice analysis outputs.
147
+
148
+ `target` labels the meaning of `Y`, typically `objs` or `cons`.
149
+ If `Y` is not provided, `target` also selects which problem output
150
+ block should be evaluated. If `index` is not `'all'`, only the
151
+ selected output columns are kept.
152
+ """
153
+ if Y is None:
154
+ Y = self.evaluate(X, target=target)
155
+ X, Y = self._checkXY(X, Y)
156
+ if target not in ("objs", "cons"):
157
+ raise ValueError("Target must be 'objs' or 'cons'.")
158
+ labels = getattr(self.problem, "objLabels" if target == "objs" else "conLabels", None)
159
+ prefix = "obj" if target == "objs" else "con"
160
+ labels = (
161
+ list(labels)
162
+ if labels is not None and len(labels) == Y.shape[1]
163
+ else [f"{prefix}{i + 1}" for i in range(Y.shape[1])]
164
+ )
165
+ if not (isinstance(index, str) and index == "all"):
166
+ indices = self._normalize_index(index)
167
+ try:
168
+ Y = Y[:, indices]
169
+ labels = [labels[i] for i in indices]
170
+ except (IndexError, TypeError):
171
+ raise ValueError("Please check the index you set!")
172
+ self.outputLabels = labels
173
+ return Y
174
+
175
+ def recordResult(self, X, Y, res, target: str = "objs", meta=None):
176
+ self.state.record(X, Y, res, target=target, meta=meta)
177
+ for metric in self.state.metrics:
178
+ for i, target in enumerate(metric.rowLabels):
179
+ self.record(target, metric.name, metric.colLabels, metric.values[i])
180
+
181
+ def record(self, target, indicator, labels, values):
182
+ """
183
+ Record the analysis results.
184
+
185
+ Args:
186
+ target: Output label such as `obj1` or `con1`.
187
+ indicator: Metric name.
188
+ labels: Input variable labels.
189
+ values: Metric values.
190
+ """
191
+
192
+ self.state.verbose.setdefault(target, {})
193
+ self.state.verbose[target].setdefault(indicator, {})
194
+
195
+ for label, v in zip(labels, values):
196
+ self.state.verbose[target][indicator][label] = v
197
+
198
+ self.state.verbose[target][indicator]["array"] = np.array(values)
199
+
200
+ def _checkXY(self, X, Y):
201
+ """
202
+ Check input and output arrays.
203
+
204
+ Args:
205
+ X: Input matrix.
206
+ Y: Output matrix.
207
+
208
+ Returns:
209
+ The validated `X` and `Y`.
210
+ """
211
+
212
+ if not isinstance(X, np.ndarray) and X is not None:
213
+ raise TypeError("X must be an instance of np.ndarray or None!")
214
+
215
+ if not isinstance(Y, np.ndarray) and Y is not None:
216
+ raise TypeError("Y must be an instance of np.ndarray or None!")
217
+
218
+ if Y.ndim == 1:
219
+ Y = Y.reshape(-1, 1)
220
+ if X is None or X.ndim != 2 or X.shape[1] != self.problem.nInput:
221
+ raise ValueError("X must have shape (nSamples, nInput).")
222
+ if Y.ndim != 2 or Y.shape[0] != X.shape[0] or Y.shape[1] == 0:
223
+ raise ValueError("Y must have one row per X sample and at least one output column.")
224
+
225
+ return X, Y
226
+
227
+ def evaluate(self, X, target: str = "objs"):
228
+ """
229
+ Evaluate the problem with the given input data.
230
+
231
+ Args:
232
+ X: Input matrix.
233
+ target: Semantic output label to evaluate, typically `objs` or `cons`.
234
+
235
+ Returns:
236
+ The requested output matrix.
237
+ """
238
+
239
+ if target not in ("objs", "cons"):
240
+ raise ValueError("Target must be 'objs' or 'cons'!")
241
+
242
+ evalRes = self.problem.evaluate(X, target=target)
243
+ Y = evalRes.objs if target == "objs" else evalRes.cons
244
+ if Y is None:
245
+ raise ValueError(f"Problem does not provide target '{target}'.")
246
+ return Y
247
+
248
+ def _normalize_index(self, index: AnaIndex):
249
+ """
250
+ Normalize output column selection into a list of integers.
251
+ """
252
+ if isinstance(index, (int, np.integer)) and not isinstance(index, (bool, np.bool_)):
253
+ return [index]
254
+ if isinstance(index, (list, tuple, np.ndarray)):
255
+ indices = list(index)
256
+ if indices and all(
257
+ isinstance(i, (int, np.integer)) and not isinstance(i, (bool, np.bool_)) for i in indices
258
+ ):
259
+ return indices
260
+ raise ValueError("Index must be 'all', an integer, or a list of integers!")
261
+
262
+ @abc.abstractmethod
263
+ def _analyzeCore(self, problem, *args, **kwargs):
264
+ pass
@@ -0,0 +1,23 @@
1
+ from .delta import DeltaTest
2
+ from .fast import FAST
3
+ from .morris import Morris
4
+ from .rbd_fast import RBDFAST
5
+ from .rsa import RSA
6
+ from .sobol import Sobol
7
+
8
+ try:
9
+ from .mars import MARS
10
+ except ModuleNotFoundError as error:
11
+ if not (error.name or "").startswith("UQPyL.surrogate.mars.core."):
12
+ raise
13
+ MARS = None
14
+
15
+ __all__ = [
16
+ "DeltaTest",
17
+ "FAST",
18
+ "MARS",
19
+ "Morris",
20
+ "RBDFAST",
21
+ "RSA",
22
+ "Sobol",
23
+ ]
@@ -0,0 +1,85 @@
1
+ import numpy as np
2
+ import warnings
3
+
4
+
5
+ def scaleOutput(values, *, fitRows=None, returnScale=False):
6
+ """Center/scale an output block, optionally fitting only selected rows.
7
+
8
+ returnScale also returns (spread, exponent), giving the physical output
9
+ scale as spread * 2**exponent without requiring that product to be finite.
10
+ """
11
+ values = np.asarray(values, dtype=float)
12
+ if not np.all(np.isfinite(values)):
13
+ raise ValueError("Sensitivity analysis requires finite output values.")
14
+ fitted = values if fitRows is None else values[fitRows]
15
+ magnitude = np.max(np.abs(fitted))
16
+ if magnitude == 0:
17
+ zeros = np.zeros_like(values)
18
+ return (zeros, (0.0, 0)) if returnScale else zeros
19
+
20
+ # Power-of-two scaling protects subtraction and preserves close float values.
21
+ exponent = int(np.frexp(magnitude)[1])
22
+ scaled = np.ldexp(values, -exponent)
23
+ if fitRows is None:
24
+ shifted = scaled - scaled.flat[0]
25
+ else:
26
+ # Retain MARS's mean-centered/max-deviation amplitude. Power-of-two
27
+ # preprocessing protects the mean while preserving ordinary fit paths.
28
+ shifted = scaled - np.mean(scaled[fitRows])
29
+ fittedShift = shifted if fitRows is None else shifted[fitRows]
30
+ spread = np.max(np.abs(fittedShift))
31
+ if spread == 0:
32
+ zeros = np.zeros_like(values)
33
+ return (zeros, (0.0, exponent)) if returnScale else zeros
34
+
35
+ shifted /= spread
36
+ result = shifted - np.mean(shifted) if fitRows is None else shifted
37
+ return (result, (float(spread), exponent)) if returnScale else result
38
+
39
+
40
+ def restoreSquaredOutput(values, scale, methodName, *, warnUnderflow=True):
41
+ """Restore squared units without forming an overflowing/underflowing scale².
42
+
43
+ The physical output scale is spread * 2**exponent. Separate mantissas
44
+ and powers also retain a tiny scaled score whose physical value is finite.
45
+ """
46
+ values = np.asarray(values, dtype=float)
47
+ spread, exponent = scale
48
+ spread, spreadPower = np.frexp(spread)
49
+ exponent += int(spreadPower)
50
+ fractions, powers = np.frexp(values)
51
+ with np.errstate(over="ignore", under="ignore", invalid="ignore"):
52
+ restored = np.ldexp(fractions * spread * spread, powers + 2 * exponent)
53
+ if not np.all(np.isfinite(restored)):
54
+ raise ValueError(f"{methodName} scores exceed the finite squared-output range.")
55
+ underflow = bool(np.any((values != 0) & (restored == 0)))
56
+ if underflow and warnUnderflow:
57
+ warnings.warn(
58
+ f"{methodName} raw scores underflow in squared output units; "
59
+ "normalized scores were computed before restoring physical units and remain available.",
60
+ RuntimeWarning,
61
+ stacklevel=3,
62
+ )
63
+ return restored, underflow
64
+
65
+
66
+ def scaleOutputColumns(values):
67
+ """Center columns independently, using one common scale to retain weights."""
68
+ values = np.asarray(values, dtype=float)
69
+ if not np.all(np.isfinite(values)):
70
+ raise ValueError("Sensitivity analysis requires finite output values.")
71
+ # Center each column in its own power-of-two units first. A huge constant
72
+ # output must not erase a tiny varying column before the common scaling.
73
+ exponents = np.frexp(np.max(np.abs(values), axis=0))[1]
74
+ shifted = np.ldexp(values, -exponents)
75
+ shifted -= shifted[:1]
76
+ columnSpreads = np.max(np.abs(shifted), axis=0)
77
+ varying = columnSpreads > 0
78
+ if not np.any(varying):
79
+ return np.zeros_like(values), (0.0, 0)
80
+ powers = np.frexp(columnSpreads)[1] + exponents
81
+ exponent = int(np.max(powers[varying]))
82
+ shifted = np.ldexp(shifted, exponents - exponent)
83
+ spread = float(np.max(np.abs(shifted)))
84
+ shifted /= spread
85
+ return shifted - np.mean(shifted, axis=0), (spread, exponent)