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.
- UQPyL/__init__.py +37 -0
- UQPyL/analysis/__init__.py +18 -0
- UQPyL/analysis/base.py +264 -0
- UQPyL/analysis/methods/__init__.py +23 -0
- UQPyL/analysis/methods/_variance.py +85 -0
- UQPyL/analysis/methods/delta.py +440 -0
- UQPyL/analysis/methods/fast.py +159 -0
- UQPyL/analysis/methods/mars.py +236 -0
- UQPyL/analysis/methods/morris.py +203 -0
- UQPyL/analysis/methods/rbd_fast.py +146 -0
- UQPyL/analysis/methods/rsa.py +199 -0
- UQPyL/analysis/methods/sobol.py +378 -0
- UQPyL/analysis/runtime/__init__.py +15 -0
- UQPyL/analysis/runtime/reader.py +113 -0
- UQPyL/analysis/runtime/result.py +153 -0
- UQPyL/analysis/runtime/storage.py +132 -0
- UQPyL/analysis/runtime/verbose.py +159 -0
- UQPyL/calibration/__init__.py +16 -0
- UQPyL/calibration/base.py +209 -0
- UQPyL/calibration/methods/__init__.py +11 -0
- UQPyL/calibration/methods/_ensemble.py +130 -0
- UQPyL/calibration/methods/_uncertainty.py +29 -0
- UQPyL/calibration/methods/es.py +152 -0
- UQPyL/calibration/methods/glue.py +107 -0
- UQPyL/calibration/methods/ies.py +297 -0
- UQPyL/calibration/methods/sufi2.py +330 -0
- UQPyL/calibration/reader.py +86 -0
- UQPyL/calibration/runtime.py +337 -0
- UQPyL/calibration/util.py +238 -0
- UQPyL/core/__init__.py +26 -0
- UQPyL/core/config.py +51 -0
- UQPyL/core/parameter_store.py +30 -0
- UQPyL/core/params.py +19 -0
- UQPyL/core/runtime.py +95 -0
- UQPyL/core/runtime_lifecycle.py +73 -0
- UQPyL/core/runtime_reader.py +82 -0
- UQPyL/core/runtime_session.py +22 -0
- UQPyL/core/runtime_storage.py +109 -0
- UQPyL/doe/__init__.py +13 -0
- UQPyL/doe/base.py +163 -0
- UQPyL/doe/methods/__init__.py +17 -0
- UQPyL/doe/methods/_sobol.py +26 -0
- UQPyL/doe/methods/fast.py +140 -0
- UQPyL/doe/methods/full_fact.py +111 -0
- UQPyL/doe/methods/lhs.py +266 -0
- UQPyL/doe/methods/morris.py +137 -0
- UQPyL/doe/methods/random.py +50 -0
- UQPyL/doe/methods/saltelli.py +152 -0
- UQPyL/doe/methods/sobol.py +99 -0
- UQPyL/inference/__init__.py +16 -0
- UQPyL/inference/base.py +475 -0
- UQPyL/inference/chain.py +40 -0
- UQPyL/inference/diagnostics.py +159 -0
- UQPyL/inference/methods/__init__.py +13 -0
- UQPyL/inference/methods/amh.py +228 -0
- UQPyL/inference/methods/demc.py +143 -0
- UQPyL/inference/methods/dream_zs.py +359 -0
- UQPyL/inference/methods/mh.py +174 -0
- UQPyL/inference/methods/mh_gibbs.py +183 -0
- UQPyL/inference/runtime/__init__.py +14 -0
- UQPyL/inference/runtime/reader.py +135 -0
- UQPyL/inference/runtime/result.py +336 -0
- UQPyL/inference/runtime/storage.py +199 -0
- UQPyL/inference/runtime/verbose.py +347 -0
- UQPyL/optimization/__init__.py +20 -0
- UQPyL/optimization/base.py +405 -0
- UQPyL/optimization/core/__init__.py +6 -0
- UQPyL/optimization/core/constraint.py +124 -0
- UQPyL/optimization/core/crowding_distance.py +57 -0
- UQPyL/optimization/core/ga_operator.py +114 -0
- UQPyL/optimization/core/non_dominated_sort.py +66 -0
- UQPyL/optimization/core/numerical.py +52 -0
- UQPyL/optimization/core/tournament.py +24 -0
- UQPyL/optimization/core/uniform_point.py +64 -0
- UQPyL/optimization/expensive/__init__.py +9 -0
- UQPyL/optimization/expensive/_base.py +74 -0
- UQPyL/optimization/expensive/asmo.py +154 -0
- UQPyL/optimization/expensive/ego.py +158 -0
- UQPyL/optimization/expensive/moasmo.py +235 -0
- UQPyL/optimization/metric/__init__.py +3 -0
- UQPyL/optimization/metric/_distance.py +49 -0
- UQPyL/optimization/metric/gd.py +5 -0
- UQPyL/optimization/metric/hv.py +198 -0
- UQPyL/optimization/metric/igd.py +5 -0
- UQPyL/optimization/moea/__init__.py +5 -0
- UQPyL/optimization/moea/moea_d.py +202 -0
- UQPyL/optimization/moea/nsga_ii.py +193 -0
- UQPyL/optimization/moea/nsga_iii.py +279 -0
- UQPyL/optimization/moea/rvea.py +240 -0
- UQPyL/optimization/population.py +212 -0
- UQPyL/optimization/runtime/__init__.py +4 -0
- UQPyL/optimization/runtime/reader.py +321 -0
- UQPyL/optimization/runtime/result.py +448 -0
- UQPyL/optimization/runtime/storage.py +259 -0
- UQPyL/optimization/runtime/verbose.py +406 -0
- UQPyL/optimization/soea/__init__.py +8 -0
- UQPyL/optimization/soea/abc.py +328 -0
- UQPyL/optimization/soea/csa.py +234 -0
- UQPyL/optimization/soea/de.py +156 -0
- UQPyL/optimization/soea/ga.py +142 -0
- UQPyL/optimization/soea/ml_sce_ua.py +219 -0
- UQPyL/optimization/soea/pso.py +203 -0
- UQPyL/optimization/soea/sce_ua.py +210 -0
- UQPyL/problem/__init__.py +73 -0
- UQPyL/problem/base.py +241 -0
- UQPyL/problem/decorators.py +26 -0
- UQPyL/problem/eval.py +38 -0
- UQPyL/problem/evaluator.py +19 -0
- UQPyL/problem/evaluator_base.py +29 -0
- UQPyL/problem/model_evaluator.py +21 -0
- UQPyL/problem/model_problem.py +254 -0
- UQPyL/problem/mop/DTLZ.py +724 -0
- UQPyL/problem/mop/ZDT.py +233 -0
- UQPyL/problem/mop/__init__.py +2 -0
- UQPyL/problem/problem.py +148 -0
- UQPyL/problem/simulator_base.py +7 -0
- UQPyL/problem/sop/__init__.py +19 -0
- UQPyL/problem/sop/single_constraint_problem.py +52 -0
- UQPyL/problem/sop/single_simple_problem.py +564 -0
- UQPyL/problem/space.py +220 -0
- UQPyL/surrogate/__init__.py +24 -0
- UQPyL/surrogate/_kernel.py +149 -0
- UQPyL/surrogate/_numeric.py +25 -0
- UQPyL/surrogate/_restart.py +40 -0
- UQPyL/surrogate/auto_tuner.py +552 -0
- UQPyL/surrogate/base.py +467 -0
- UQPyL/surrogate/gp/__init__.py +2 -0
- UQPyL/surrogate/gp/gaussian_process.py +268 -0
- UQPyL/surrogate/gp/kernel/__init__.py +7 -0
- UQPyL/surrogate/gp/kernel/base_kernel.py +61 -0
- UQPyL/surrogate/gp/kernel/c_kernel_.py +30 -0
- UQPyL/surrogate/gp/kernel/dot_kernel_.py +33 -0
- UQPyL/surrogate/gp/kernel/matern_kernel.py +167 -0
- UQPyL/surrogate/gp/kernel/rbf_kernel.py +44 -0
- UQPyL/surrogate/gp/kernel/rq_kernel.py +55 -0
- UQPyL/surrogate/kriging/__init__.py +2 -0
- UQPyL/surrogate/kriging/kernel/__init__.py +4 -0
- UQPyL/surrogate/kriging/kernel/base_kernel.py +36 -0
- UQPyL/surrogate/kriging/kernel/cubic_kernel.py +33 -0
- UQPyL/surrogate/kriging/kernel/exp_kernel.py +32 -0
- UQPyL/surrogate/kriging/kernel/guass_kernel.py +32 -0
- UQPyL/surrogate/kriging/kriging.py +326 -0
- UQPyL/surrogate/mars/__init__.py +1 -0
- UQPyL/surrogate/mars/core/__init__.py +4 -0
- UQPyL/surrogate/mars/core/_basis.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_basis.pxd +239 -0
- UQPyL/surrogate/mars/core/_basis.pyx +1120 -0
- UQPyL/surrogate/mars/core/_forward.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_forward.pxd +93 -0
- UQPyL/surrogate/mars/core/_forward.pyx +579 -0
- UQPyL/surrogate/mars/core/_knot_search.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_knot_search.pxd +94 -0
- UQPyL/surrogate/mars/core/_knot_search.pyx +678 -0
- UQPyL/surrogate/mars/core/_pruning.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_pruning.pxd +25 -0
- UQPyL/surrogate/mars/core/_pruning.pyx +215 -0
- UQPyL/surrogate/mars/core/_qr.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_qr.pxd +34 -0
- UQPyL/surrogate/mars/core/_qr.pyx +264 -0
- UQPyL/surrogate/mars/core/_record.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_record.pxd +66 -0
- UQPyL/surrogate/mars/core/_record.pyx +332 -0
- UQPyL/surrogate/mars/core/_types.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_types.pxd +5 -0
- UQPyL/surrogate/mars/core/_types.pyx +5 -0
- UQPyL/surrogate/mars/core/_util.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/mars/core/_util.pxd +23 -0
- UQPyL/surrogate/mars/core/_util.pyx +94 -0
- UQPyL/surrogate/mars/mars.py +804 -0
- UQPyL/surrogate/metric.py +74 -0
- UQPyL/surrogate/poly.py +62 -0
- UQPyL/surrogate/rbf/__init__.py +2 -0
- UQPyL/surrogate/rbf/kernel/__init__.py +8 -0
- UQPyL/surrogate/rbf/kernel/base_kernel.py +75 -0
- UQPyL/surrogate/rbf/kernel/cubic_kernel.py +33 -0
- UQPyL/surrogate/rbf/kernel/gaussian_kernel.py +21 -0
- UQPyL/surrogate/rbf/kernel/linear_kernel.py +30 -0
- UQPyL/surrogate/rbf/kernel/multiquadric_kernel.py +31 -0
- UQPyL/surrogate/rbf/kernel/thin_plate_spline_kernel.py +37 -0
- UQPyL/surrogate/rbf/radial_basis_function.py +197 -0
- UQPyL/surrogate/regression/__init__.py +2 -0
- UQPyL/surrogate/regression/lasso/__init__.py +8 -0
- UQPyL/surrogate/regression/lasso/cython_utils.pxd +54 -0
- UQPyL/surrogate/regression/lasso/cython_utils.pyx +472 -0
- UQPyL/surrogate/regression/lasso/lasso.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/regression/lasso/lasso_fast.pyx +852 -0
- UQPyL/surrogate/regression/linear_regression.py +275 -0
- UQPyL/surrogate/regression/polynomial_regression.py +151 -0
- UQPyL/surrogate/scaler.py +178 -0
- UQPyL/surrogate/setting.py +376 -0
- UQPyL/surrogate/split.py +57 -0
- UQPyL/surrogate/svr/__init__.py +1 -0
- UQPyL/surrogate/svr/core/__init__.py +7 -0
- UQPyL/surrogate/svr/core/libsvm_interface.cp314-win_amd64.pyd +0 -0
- UQPyL/surrogate/svr/core/libsvm_interface.cpp +147 -0
- UQPyL/surrogate/svr/core/svm.cpp +3329 -0
- UQPyL/surrogate/svr/core/svm.h +108 -0
- UQPyL/surrogate/svr/support_vector_machine.py +240 -0
- UQPyL/surrogate/util/__init__.py +0 -0
- UQPyL/surrogate/util/boxmin.py +147 -0
- UQPyL/surrogate/util/lbfgsb.py +67 -0
- UQPyL/viz/__init__.py +23 -0
- UQPyL/viz/analysis.py +91 -0
- UQPyL/viz/common.py +29 -0
- UQPyL/viz/inference.py +184 -0
- UQPyL/viz/optimization.py +319 -0
- UQPyL/viz/surrogate.py +68 -0
- uqpyl-2.1.7.dist-info/METADATA +346 -0
- uqpyl-2.1.7.dist-info/RECORD +212 -0
- uqpyl-2.1.7.dist-info/WHEEL +5 -0
- uqpyl-2.1.7.dist-info/licenses/LICENSE.md +21 -0
- 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)
|