-
Notifications
You must be signed in to change notification settings - Fork 32
Expand file tree
/
Copy pathconfig_validation.py
More file actions
123 lines (107 loc) · 4.79 KB
/
Copy pathconfig_validation.py
File metadata and controls
123 lines (107 loc) · 4.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
"""
Module for validation of config (typically read from Excel).
"""
import copy
import numbers
from enum import StrEnum
from typing import Any
class SeedStrategy(StrEnum):
"""How Monte Carlo samples are seeded.
JOINT:
The parameters of a sensitivity are drawn in one Latin Hypercube
Sampling call (the default). Adding, removing or reordering a parameter
reshuffles every other parameter *in that sensitivity*. Sensitivities do
not affect each other, since each sampling call gets its own generator,
but adding or removing a whole sensitivity shifts the ones after it.
INDEPENDENT:
Each parameter, and each correlation group, is seeded separately from
the base seed, so changing one leaves the others bit-identical. The key
includes the sensitivity and parameter names, so renaming either
reshuffles the values it covers.
"""
JOINT = "joint"
INDEPENDENT = "independent"
def validate_configuration(
config: dict[str, Any], verbosity: int = 0
) -> dict[str, Any]:
"""Main function for config validation.
This function is responsible for:
- Checking that required keys exist
- Checking that values are set to valid types
- Setting default values if keys are not set
"""
config = copy.deepcopy(config)
if config["designtype"] != "onebyone":
raise ValueError(
"Generation of DesignMatrix only implemented for type 'onebyone', "
f"not {config['designtype']}"
)
if "repeats" not in config:
raise LookupError('"repeats" must be specified in general input sheet')
key = "correlation_iterations"
if key not in config:
if verbosity > 0:
print(f"{key!r} not set in general input sheet. Setting to default 0.")
print(" - When set to 0, Iman Conover is used to induce correlations.")
print(
" - When set to a positive integer N, Iman Conover is followed by N iterations\n" # ruff: ignore[line-too-long]
" of random permutations (swaps). This leads to results that are never worse, and often better.\n" # ruff: ignore[line-too-long]
" It is especially useful for skewed distributions like lognormal and high dimensional problems." # ruff: ignore[line-too-long]
)
print(
f" If desired correlation does not match observed, try setting {key!r}=999 or higher." # ruff: ignore[line-too-long]
)
config[key] = 0
else:
try:
config[key] = int(config[key])
except (ValueError, TypeError) as err:
raise ValueError(
f"{key!r} must be a non-negative integer, got: {config[key]}"
) from err
key = "distribution_seed"
if key not in config:
raise ValueError(
"You did not specify a value for 'distribution_seed', which is used to "
"seed the random number generator that draws from distributions in Monte "
"Carlo sensitivities.\n"
"- Specify a number (e.g. a 6 digit integer) to seed the random number "
"generator and obtain reproducible results.\n"
"- Specify None if you do not want to seed the random number generator. "
"Your analysis will not be reproducible."
)
if not (isinstance(config[key], numbers.Integral) or (config[key] is None)):
raise ValueError(
f"{key!r} must be a non-negative integer or None, got: {config[key]}"
)
# 'seed_strategy' controls how Monte Carlo samples are seeded.
# See the SeedStrategy docstring for what each strategy means.
key = "seed_strategy"
value = config.get(key)
if isinstance(value, str):
value = value.strip().lower()
if value is None or value == "none":
value = SeedStrategy.JOINT
try:
config[key] = SeedStrategy(value)
except (ValueError, TypeError) as err:
raise ValueError(
f"{key!r} must be one of {[s.value for s in SeedStrategy]}, "
f"got: {config[key]}"
) from err
# 'seeds' here is 'rms_seeds' in the input. It can be either:
# - 'default' => gives seed numbers 1000, 1001, 1002, ...
# - 'None' => seed number not added
# - a path to a file
key = "seeds"
if key not in config:
msg = '"rms_seeds" must be specified in general input sheet\n'
msg += ' - Set to "None", "default" or path to a file.'
raise LookupError(msg)
is_default = config[key] == "default"
is_none = config[key] is None
is_list = isinstance(config[key], list) and config[key]
if not any([is_default, is_none, is_list]):
msg = f"'rms_seeds' must be 'None', 'default' or a list, got: {config[key]}"
raise ValueError(msg)
return config