Skip to content

Commit

Permalink
Merge pull request #39 from wetransform-os/add-an-option-to-specify-r…
Browse files Browse the repository at this point in the history
…andom-seeds

Add selection of random seed for reproducibility
  • Loading branch information
Flaminietta authored Mar 15, 2024
2 parents 01e91d8 + 64bd5b6 commit 3dc78e4
Show file tree
Hide file tree
Showing 11 changed files with 58 additions and 23 deletions.
1 change: 1 addition & 0 deletions configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"monte_carlo_sampling": {
"monte_carlo_runs": 10000,
"num_cores": 1,
"random_seed": 67,
"marginal_distribution_for_each_indicator": ["normal", "normal", "normal", "normal", "normal", "normal"]},


Expand Down
4 changes: 2 additions & 2 deletions mcda/configuration/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ class Config:
_str_values: List[str] = ['input_matrix_path', 'output_path', 'sensitivity_on', 'normalization', 'aggregation',
'robustness_on', 'on_single_weights', 'on_all_weights', 'given_weights', 'on_indicators']

_int_values: List[str] = ['monte_carlo_runs', 'num_cores']
_int_values: List[str] = ['monte_carlo_runs', 'num_cores', 'random_seed']

_dict_values: List[str] = ['sensitivity', 'robustness', 'monte_carlo_sampling']

_keys_of_dict_values = {'sensitivity': ['sensitivity_on', 'normalization', 'aggregation'],
'robustness': ['robustness_on', 'on_single_weights', 'on_all_weights',
'given_weights', 'on_indicators'],
'monte_carlo_sampling': ['monte_carlo_runs', 'num_cores',
'monte_carlo_sampling': ['monte_carlo_runs', 'num_cores', 'random_seed',
'marginal_distribution_for_each_indicator']}

def __init__(self, input_config: dict):
Expand Down
11 changes: 7 additions & 4 deletions mcda/mcda_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def main(input_config: dict):
is_sensitivity = config.sensitivity['sensitivity_on']
is_robustness = config.robustness['robustness_on']
mc_runs = config.monte_carlo_sampling["monte_carlo_runs"]
random_seed = config.monte_carlo_sampling["random_seed"]

# Check for sensitivity-related configuration errors
if is_sensitivity == "no":
Expand Down Expand Up @@ -122,7 +123,7 @@ def main(input_config: dict):
is_robustness_weights, is_robustness_indicators = \
check_config_setting(condition_robustness_on_weights,
condition_robustness_on_indicators,
mc_runs)
mc_runs, random_seed)

marginal_pdf = config.monte_carlo_sampling["marginal_distribution_for_each_indicator"]
logger.info("Read input matrix with uncertainty of the indicators at {}".format(
Expand Down Expand Up @@ -151,11 +152,13 @@ def main(input_config: dict):
# If there is no uncertainty of the indicators:
if is_robustness_indicators == 0:
run_mcda_without_indicator_uncertainty(input_config, index_column_name, index_column_values,
input_matrix_no_alternatives, weights, f_norm, f_agg, is_robustness_weights)
input_matrix_no_alternatives, weights, f_norm, f_agg,
is_robustness_weights)
# else (i.e. there is uncertainty):
else:
run_mcda_with_indicator_uncertainty(input_config, input_matrix_no_alternatives, index_column_name, index_column_values,
mc_runs, is_sensitivity, f_agg, f_norm, weights, polar, marginal_pdf)
run_mcda_with_indicator_uncertainty(input_config, input_matrix_no_alternatives, index_column_name,
index_column_values, mc_runs, random_seed, is_sensitivity, f_agg, f_norm,
weights, polar, marginal_pdf)

logger.info("ProMCDA finished calculations: check the output files")
elapsed = time.time() - t
Expand Down
17 changes: 14 additions & 3 deletions mcda/mcda_with_robustness.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ class MCDAWithRobustness:
"""

def __init__(self, config: Config, input_matrix: pd.DataFrame, is_exact_pdf_mask=None, is_poisson_pdf_mask=None):

def __init__(self, config: Config, input_matrix: pd.DataFrame(), is_exact_pdf_mask=None, is_poisson_pdf_mask=None,
random_seed=None):
self.is_exact_pdf_mask = is_exact_pdf_mask
self.is_poisson_pdf_mask = is_poisson_pdf_mask
self.random_seed = random_seed
self._config = copy.deepcopy(config)
self._input_matrix = copy.deepcopy(input_matrix)

Expand Down Expand Up @@ -101,8 +104,16 @@ def create_n_randomly_sampled_matrices(self) -> List[pd.DataFrame]:
input_matrix = self._input_matrix # (AxnI)
is_exact_pdf_mask = self.is_exact_pdf_mask
is_poisson_pdf_mask = self.is_poisson_pdf_mask

np.random.seed(42)
random_seed = self.random_seed

if random_seed is not None:
np.random.seed(random_seed)
else:
# TODO: the default_random_seed cannot be used while reading the settings from a JSON file, where None
# cannot be given as an option; it will be implemented when the congiguration settings will be passed as a
# stream or handle.
default_random_seed = 42
np.random.seed(default_random_seed)

sampled_matrices = [] # list long I

Expand Down
34 changes: 20 additions & 14 deletions mcda/utils/utils_for_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,32 +45,41 @@ def check_config_error(condition: bool, error_message: str):
raise ValueError(error_message)


def check_config_setting(condition_robustness_on_weights: bool, condition_robustness_on_indicators: bool, mc_runs: int)\
-> (int, int):

def check_config_setting(condition_robustness_on_weights: bool, condition_robustness_on_indicators: bool, mc_runs: int,
random_seed: int) -> (int, int):
"""
Checks configuration settings and logs information messages.
Return:
- is_robustness_weights, is_robustness_indicators, a tuple containing flags indicating robustness on weights and
indicators.
Returns:
- is_robustness_weights, is_robustness_indicators, booleans indicating if robustness is considered
on weights or indicators.
Example:
```python
is_robustness_weights, is_robustness_indicators = check_config_setting(True, False, 1000, 42)
```
:param condition_robustness_on_weights: bool
:param condition_robustness_on_indicators: bool
:param mc_runs: int
:param random_seed: int
:return: (is_robustness_weights, is_robustness_indicators)
:rtype: Tuple[int, int]
"""

is_robustness_weights = 0
is_robustness_indicators = 0

if condition_robustness_on_weights:
logger.info("ProMCDA will consider uncertainty on the weights.")
logger.info("Number of Monte Carlo runs: {}".format(mc_runs))
logger.info("The random seed used is: {}".format(random_seed))
is_robustness_weights = 1

elif condition_robustness_on_indicators:
logger.info("ProMCDA will consider uncertainty on the indicators.")
logger.info("Number of Monte Carlo runs: {}".format(mc_runs))
logger.info("The random seed used is: {}".format(random_seed))
is_robustness_indicators = 1

return is_robustness_weights, is_robustness_indicators
Expand Down Expand Up @@ -259,7 +268,6 @@ def check_indicator_weights_polarities(num_indicators: int, polar: List[str], co
:param config: dict
:return: None
"""

if num_indicators != len(polar):
raise ValueError('The number of polarities does not correspond to the no. of indicators')

Expand Down Expand Up @@ -875,11 +883,9 @@ def run_mcda_without_indicator_uncertainty(input_config: dict, index_column_name
input_matrix=input_matrix, config=input_config,
is_robustness_weights=is_robustness_weights)


def run_mcda_with_indicator_uncertainty(input_config: dict, input_matrix: pd.DataFrame,
index_column_name: str, index_column_values: list,
mc_runs: int, is_sensitivity: str, f_agg: str, f_norm: str,
weights: Union[List[list], List[pd.DataFrame], dict],
def run_mcda_with_indicator_uncertainty(input_config: dict, input_matrix: pd.DataFrame, index_column_name: str,
index_column_values: list, mc_runs: int, random_seed: int, is_sensitivity: str,
f_agg: str, f_norm: str, weights: Union[List[list], List[pd.DataFrame], dict],
polar: List[str], marginal_pdf: List[str]) -> None:
"""
Runs ProMCDA with uncertainty on the indicators, i.e. with a robustness analysis.
Expand Down Expand Up @@ -924,7 +930,7 @@ def run_mcda_with_indicator_uncertainty(input_config: dict, input_matrix: pd.Dat
is_exact_pdf_mask = check_if_pdf_is_exact(marginal_pdf)
is_poisson_pdf_mask = check_if_pdf_is_poisson(marginal_pdf)

mcda_with_uncert = MCDAWithRobustness(config, input_matrix, is_exact_pdf_mask, is_poisson_pdf_mask)
mcda_with_uncert = MCDAWithRobustness(config, input_matrix, is_exact_pdf_mask, is_poisson_pdf_mask, random_seed)
n_random_input_matrices = mcda_with_uncert.create_n_randomly_sampled_matrices()

if is_sensitivity == "yes":
Expand Down
3 changes: 3 additions & 0 deletions tests/unit_tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def get_correct_config():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['norm', 'lnorm', 'beta']},
"output_path": "/path/to/output"
}
Expand All @@ -46,6 +47,7 @@ def get_uncorrect_config_1():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['norm', 'lnorm', 'beta']},
"output_path": "/path/to/output"
}
Expand All @@ -68,6 +70,7 @@ def get_uncorrect_config_2():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['norm', 'lnorm', 'beta']},
"output_path": "/path/to/output"
}
Expand Down
4 changes: 4 additions & 0 deletions tests/unit_tests/test_mcda_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ def get_incorrect_config_1():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ["exact", "exact", "exact", "exact", "exact", "exact"]},

"output_path": "/path/to/output"
Expand Down Expand Up @@ -61,6 +62,7 @@ def get_incorrect_config_2():
"monte_carlo_sampling": {
"monte_carlo_runs": 0,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ["norm", "exact", "lnorm", "exact", "poisson", "exact"]},

"output_path": "/path/to/output"
Expand All @@ -87,6 +89,7 @@ def get_incorrect_config_3():
"monte_carlo_sampling": {
"monte_carlo_runs": 10000,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ["exact", "exact", "exact", "exact", "exact", "exact"]},

"output_path": "/path/to/output"
Expand All @@ -110,6 +113,7 @@ def get_correct_config():
"monte_carlo_sampling": {
"monte_carlo_runs": 1000,
"num_cores": 1,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['uniform', 'exact', 'normal', 'normal', 'exact',
'lnorm']},
"output_path": "/path/to/output"
Expand Down
1 change: 1 addition & 0 deletions tests/unit_tests/test_mcda_with_robustness.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def get_test_config():
"monte_carlo_sampling": {
"monte_carlo_runs": 100,
"num_cores": 1,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['exact', 'uniform', 'normal', 'poisson']},
"output_path": "/path/to/output"
}
Expand Down
4 changes: 4 additions & 0 deletions tests/unit_tests/test_mcda_without_robustness.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ def get_test_config():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['exact', 'exact', 'exact', 'exact', 'exact', 'exact']},
"output_path": "/path/to/output"
}
Expand All @@ -47,6 +48,7 @@ def get_test_config_simple_mcda():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['exact', 'exact', 'exact', 'exact', 'exact', 'exact']},
"output_path": "/path/to/output"
}
Expand All @@ -69,6 +71,7 @@ def get_test_config_randomness():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['exact', 'exact', 'exact', 'exact', 'exact', 'exact']},
"output_path": "/path/to/output"
}
Expand All @@ -91,6 +94,7 @@ def get_test_config_randomness_simple_mcda():
"monte_carlo_sampling": {
"monte_carlo_runs": 10,
"num_cores": 4,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['exact', 'exact', 'exact', 'exact', 'exact', 'exact']},
"output_path": "/path/to/output"
}
Expand Down
1 change: 1 addition & 0 deletions tests/unit_tests/test_utils_for_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def get_test_config():
"monte_carlo_sampling": {
"monte_carlo_runs": 10000,
"num_cores": 1,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['exact', 'uniform', 'normal', 'poisson']},
"output_path": "/path/to/output"
}
Expand Down
1 change: 1 addition & 0 deletions tests/unit_tests/test_utils_for_parallelization.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def get_test_config():
"monte_carlo_sampling": {
"monte_carlo_runs": 10000,
"num_cores": 1,
"random_seed": 42,
"marginal_distribution_for_each_indicator": ['exact', 'uniform', 'normal', 'exact', 'uniform']},
"output_path": "/path/to/output"
}
Expand Down

0 comments on commit 3dc78e4

Please sign in to comment.