From 9a8a1fd710a4c1c5760479cc60203896a899dac0 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Tue, 13 May 2025 17:21:29 +0100 Subject: [PATCH 1/3] Adds code to save and load results objects --- RATapi/__init__.py | 15 ++- RATapi/outputs.py | 245 ++++++++++++++++++++++++++++++++++++++++++ RATapi/project.py | 1 - tests/test_outputs.py | 23 ++++ tests/utils.py | 69 ++---------- 5 files changed, 289 insertions(+), 64 deletions(-) diff --git a/RATapi/__init__.py b/RATapi/__init__.py index 3d74927d..31f83c85 100644 --- a/RATapi/__init__.py +++ b/RATapi/__init__.py @@ -6,6 +6,7 @@ from RATapi import events, models from RATapi.classlist import ClassList from RATapi.controls import Controls +from RATapi.outputs import BayesResults, Results from RATapi.project import Project from RATapi.run import run from RATapi.utils import convert, plotting @@ -13,4 +14,16 @@ with suppress(ImportError): # orsopy is an optional dependency from RATapi.utils import orso as orso -__all__ = ["examples", "models", "events", "ClassList", "Controls", "Project", "run", "plotting", "convert"] +__all__ = [ + "examples", + "models", + "events", + "ClassList", + "Controls", + "BayesResults", + "Results", + "Project", + "run", + "plotting", + "convert", +] diff --git a/RATapi/outputs.py b/RATapi/outputs.py index 925db7c5..afad575b 100644 --- a/RATapi/outputs.py +++ b/RATapi/outputs.py @@ -1,6 +1,8 @@ """Converts results from the compiled RAT code to python dataclasses.""" +import json from dataclasses import dataclass +from pathlib import Path from typing import Any, Optional, Union import numpy as np @@ -8,6 +10,69 @@ import RATapi.rat_core from RATapi.utils.enums import Procedures +bayes_results_subclasses = [ + "predictionIntervals", + "confidenceIntervals", + "dreamParams", + "dreamOutput", + "nestedSamplerOutput", +] + +bayes_results_fields = { + "param_fields": { + "predictionIntervals": [], + "confidenceIntervals": [], + "dreamParams": [ + "nParams", + "nChains", + "nGenerations", + "parallel", + "CPU", + "jumpProbability", + "pUnitGamma", + "nCR", + "delta", + "steps", + "zeta", + "outlier", + "adaptPCR", + "thinning", + "epsilon", + "ABC", + "IO", + "storeOutput", + ], + "dreamOutput": ["runtime", "iteration"], + "nestedSamplerOutput": ["logZ", "logZErr"], + }, + "list_fields": { + "predictionIntervals": ["reflectivity"], + "confidenceIntervals": [], + "dreamParams": [], + "dreamOutput": [], + "nestedSamplerOutput": [], + }, + "double_list_fields": { + "predictionIntervals": ["sld"], + "confidenceIntervals": [], + "dreamParams": [], + "dreamOutput": [], + "nestedSamplerOutput": [], + }, + "array_fields": { + "predictionIntervals": ["sampleChi"], + "confidenceIntervals": ["percentile65", "percentile95", "mean"], + "dreamParams": ["R"], + "dreamOutput": ["allChains", "outlierChains", "AR", "R_stat", "CR"], + "nestedSamplerOutput": ["nestSamples", "postSamples"], + }, +} + +results_fields = { + "list_fields": ["reflectivity", "simulation", "shiftedData", "backgrounds", "resolutions"], + "double_list_fields": ["sldProfiles", "layers", "resampledLayers"], +} + def get_field_string(field: str, value: Any, array_limit: int): """Return a string representation of class fields where large arrays are represented by their shape. @@ -179,6 +244,49 @@ def __str__(self): output += get_field_string(key, value, 100) return output + def save(self, filepath: Union[str, Path] = "./results.json"): + """Save the Results object to a JSON file. + + Parameters + ---------- + filepath : str or Path + The path to where the results file will be written. + """ + filepath = Path(filepath).with_suffix(".json") + json_dict = write_core_results_fields(self) + + filepath.write_text(json.dumps(json_dict)) + + @classmethod + def load(cls, path: Union[str, Path]) -> "Results": + """Load a Results object from file. + + Parameters + ---------- + path : str or Path + The path to the results json file. + """ + path = Path(path) + input_data = path.read_text() + results_dict = json.loads(input_data) + + results_dict = read_core_results_fields(results_dict) + + return Results( + reflectivity=results_dict["reflectivity"], + simulation=results_dict["simulation"], + shiftedData=results_dict["shiftedData"], + backgrounds=results_dict["backgrounds"], + resolutions=results_dict["resolutions"], + sldProfiles=results_dict["sldProfiles"], + layers=results_dict["layers"], + resampledLayers=results_dict["resampledLayers"], + calculationResults=CalculationResults(**results_dict["calculationResults"]), + contrastParams=ContrastParams(**results_dict["contrastParams"]), + fitParams=np.array(results_dict["fitParams"]), + fitNames=results_dict["fitNames"], + ) + @dataclass class PredictionIntervals(RATResult): @@ -405,6 +513,143 @@ class BayesResults(Results): nestedSamplerOutput: NestedSamplerOutput chain: np.ndarray + def save(self, filepath: Union[str, Path] = "./results.json"): + """Save the BayesResults object to a JSON file. + + Parameters + ---------- + filepath : str or Path + The path to where the results file will be written. + """ + filepath = Path(filepath).with_suffix(".json") + json_dict = write_core_results_fields(self) + + # Take each of the subclasses in a BayesResults instance and switch the numpy arrays to lists + for subclass_name in bayes_results_subclasses: + subclass = getattr(self, subclass_name) + subclass_dict = {} + + for field in bayes_results_fields["param_fields"][subclass_name]: + subclass_dict[field] = getattr(subclass, field) + + for field in bayes_results_fields["list_fields"][subclass_name]: + subclass_dict[field] = [result_array.tolist() for result_array in getattr(subclass, field)] + + for field in bayes_results_fields["double_list_fields"][subclass_name]: + subclass_dict[field] = [ + [result_array.tolist() for result_array in inner_list] for inner_list in getattr(subclass, field) + ] + + for field in bayes_results_fields["array_fields"][subclass_name]: + subclass_dict[field] = getattr(subclass, field).tolist() + + json_dict[subclass_name] = subclass_dict + + json_dict["chain"] = self.chain.tolist() + filepath.write_text(json.dumps(json_dict)) + + @classmethod + def load(cls, path: Union[str, Path]) -> "BayesResults": + """Load a BayesResults object from file. + + Parameters + ---------- + path : str or Path + The path to the results json file. + """ + path = Path(path) + input_data = path.read_text() + results_dict = json.loads(input_data) + + results_dict = read_core_results_fields(results_dict) + + # Take each of the subclasses in a BayesResults instance and convert to numpy arrays where necessary + for subclass_name in bayes_results_subclasses: + subclass_dict = {} + + for field in bayes_results_fields["param_fields"][subclass_name]: + subclass_dict[field] = results_dict[subclass_name][field] + + for field in bayes_results_fields["list_fields"][subclass_name]: + subclass_dict[field] = [np.array(result_array) for result_array in results_dict[subclass_name][field]] + + for field in bayes_results_fields["double_list_fields"][subclass_name]: + subclass_dict[field] = [ + [np.array(result_array) for result_array in inner_list] + for inner_list in results_dict[subclass_name][field] + ] + + for field in bayes_results_fields["array_fields"][subclass_name]: + subclass_dict[field] = np.array(results_dict[subclass_name][field]) + + results_dict[subclass_name] = subclass_dict + + return BayesResults( + reflectivity=results_dict["reflectivity"], + simulation=results_dict["simulation"], + shiftedData=results_dict["shiftedData"], + backgrounds=results_dict["backgrounds"], + resolutions=results_dict["resolutions"], + sldProfiles=results_dict["sldProfiles"], + layers=results_dict["layers"], + resampledLayers=results_dict["resampledLayers"], + calculationResults=CalculationResults(**results_dict["calculationResults"]), + contrastParams=ContrastParams(**results_dict["contrastParams"]), + fitParams=np.array(results_dict["fitParams"]), + fitNames=results_dict["fitNames"], + predictionIntervals=PredictionIntervals(**results_dict["predictionIntervals"]), + confidenceIntervals=ConfidenceIntervals(**results_dict["confidenceIntervals"]), + dreamParams=DreamParams(**results_dict["dreamParams"]), + dreamOutput=DreamOutput(**results_dict["dreamOutput"]), + nestedSamplerOutput=NestedSamplerOutput(**results_dict["nestedSamplerOutput"]), + chain=np.array(results_dict["chain"]), + ) + + +def write_core_results_fields(results: Union[Results, BayesResults], json_dict: Optional[dict] = None) -> dict: + """Modify the values of the fields that appear in both Results and BayesResults when saving to a json file.""" + if json_dict is None: + json_dict = {} + + for field in results_fields["list_fields"]: + json_dict[field] = [result_array.tolist() for result_array in getattr(results, field)] + + for field in results_fields["double_list_fields"]: + json_dict[field] = [ + [result_array.tolist() for result_array in inner_list] for inner_list in getattr(results, field) + ] + + json_dict["calculationResults"] = {} + json_dict["calculationResults"]["chiValues"] = results.calculationResults.chiValues.tolist() + json_dict["calculationResults"]["sumChi"] = results.calculationResults.sumChi + + json_dict["contrastParams"] = {} + for field in results.contrastParams.__dict__: + json_dict["contrastParams"][field] = getattr(results.contrastParams, field).tolist() + + json_dict["fitParams"] = results.fitParams.tolist() + json_dict["fitNames"] = results.fitNames + + return json_dict + + +def read_core_results_fields(results_dict: dict) -> dict: + """Modify the values of the fields that appear in both Results and BayesResults when loading a json file.""" + for field in results_fields["list_fields"]: + results_dict[field] = [np.array(result_array) for result_array in results_dict[field]] + + for field in results_fields["double_list_fields"]: + results_dict[field] = [ + [np.array(result_array) for result_array in inner_list] for inner_list in results_dict[field] + ] + + results_dict["calculationResults"]["chiValues"] = np.array(results_dict["calculationResults"]["chiValues"]) + + for field in results_dict["contrastParams"]: + results_dict["contrastParams"][field] = np.array(results_dict["contrastParams"][field]) + + return results_dict + def make_results( procedure: Procedures, diff --git a/RATapi/project.py b/RATapi/project.py index f9fd30b9..c35e92a6 100644 --- a/RATapi/project.py +++ b/RATapi/project.py @@ -906,7 +906,6 @@ def save(self, filepath: Union[str, Path] = "./project.json"): ---------- filepath : str or Path The path to where the project file will be written. - """ filepath = Path(filepath).with_suffix(".json") diff --git a/tests/test_outputs.py b/tests/test_outputs.py index 37a4c631..5cd97fc4 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -3,6 +3,9 @@ We use the example for both a reflectivity calculation, and Bayesian analysis using the Dream algorithm. """ +import tempfile +from pathlib import Path + import numpy as np import pytest @@ -203,3 +206,23 @@ def test_results_str(test_output_results, test_str, request) -> None: test_str = request.getfixturevalue(test_str) assert test_output_results.__str__() == test_str + + +@pytest.mark.parametrize( + ["result_class", "test_results"], + [ + (RATapi.Results, "reflectivity_calculation_results"), + (RATapi.BayesResults, "dream_results"), + ], +) +def test_save_load(result_class, test_results, request): + """Test that saving and loading an output object returns the same object.""" + test_results = request.getfixturevalue(test_results) + + with tempfile.TemporaryDirectory() as tmp: + # ignore relative path warnings + path = Path(tmp, "results.json") + test_results.save(path) + loaded_results = result_class.load(path) + + check_results_equal(test_results, loaded_results) diff --git a/tests/utils.py b/tests/utils.py index 40524fd0..d293d7b1 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -30,8 +30,6 @@ def check_results_equal(actual_results, expected_results) -> None: We focus here on the fields common to both results objects, and also check the equality of the subclasses "CalculationResults" and "ContrastParams". """ - list_fields = ["reflectivity", "simulation", "shiftedData", "backgrounds", "resolutions"] - double_list_fields = ["sldProfiles", "layers", "resampledLayers"] contrast_param_fields = [ "scalefactors", "bulkIn", @@ -49,11 +47,11 @@ def check_results_equal(actual_results, expected_results) -> None: # The first set of fields are either 1D or 2D python lists containing numpy arrays. # Hence, we need to compare them element-wise. - for list_field in list_fields: + for list_field in RATapi.outputs.results_fields["list_fields"]: for a, b in zip(getattr(actual_results, list_field), getattr(expected_results, list_field)): assert (a == b).all() - for list_field in double_list_fields: + for list_field in RATapi.outputs.results_fields["double_list_fields"]: actual_list = getattr(actual_results, list_field) expected_list = getattr(expected_results, list_field) assert len(actual_list) == len(expected_list) @@ -84,71 +82,18 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: We focus here on the fields and subclasses specific to the Bayesian optimisation. """ # The BayesResults object consists of a number of subclasses, each containing fields of differing formats. - subclasses = ["predictionIntervals", "confidenceIntervals", "dreamParams", "dreamOutput", "nestedSamplerOutput"] - - param_fields = { - "predictionIntervals": [], - "confidenceIntervals": [], - "dreamParams": [ - "nParams", - "nChains", - "nGenerations", - "parallel", - "CPU", - "jumpProbability", - "pUnitGamma", - "nCR", - "delta", - "steps", - "zeta", - "outlier", - "adaptPCR", - "thinning", - "epsilon", - "ABC", - "IO", - "storeOutput", - ], - "dreamOutput": ["runtime", "iteration"], - "nestedSamplerOutput": ["logZ"], - } - - list_fields = { - "predictionIntervals": ["reflectivity"], - "confidenceIntervals": [], - "dreamParams": [], - "dreamOutput": [], - "nestedSamplerOutput": [], - } - - double_list_fields = { - "predictionIntervals": ["sld"], - "confidenceIntervals": [], - "dreamParams": [], - "dreamOutput": [], - "nestedSamplerOutput": [], - } - - array_fields = { - "predictionIntervals": ["sampleChi"], - "confidenceIntervals": ["percentile65", "percentile95", "mean"], - "dreamParams": ["R"], - "dreamOutput": ["allChains", "outlierChains", "AR", "R_stat", "CR"], - "nestedSamplerOutput": ["nestSamples", "postSamples"], - } - - for subclass in subclasses: + for subclass in RATapi.outputs.bayes_results_subclasses: actual_subclass = getattr(actual_results, subclass) expected_subclass = getattr(expected_results, subclass) - for field in param_fields[subclass]: + for field in RATapi.outputs.bayes_results_fields["param_fields"][subclass]: assert getattr(actual_subclass, field) == getattr(expected_subclass, field) - for field in list_fields[subclass]: + for field in RATapi.outputs.bayes_results_fields["list_fields"][subclass]: for a, b in zip(getattr(actual_subclass, field), getattr(expected_subclass, field)): assert (a == b).all() - for field in double_list_fields[subclass]: + for field in RATapi.outputs.bayes_results_fields["double_list_fields"][subclass]: actual_list = getattr(actual_subclass, field) expected_list = getattr(expected_subclass, field) assert len(actual_list) == len(expected_list) @@ -157,7 +102,7 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: assert (a == b).all() # Need to account for the arrays that are initialised as "NaN" in the compiled code - for array in array_fields[subclass]: + for array in RATapi.outputs.bayes_results_fields["array_fields"][subclass]: actual_array = getattr(actual_subclass, array) expected_array = getattr(expected_subclass, array) for i in range(len(actual_array)): From c3b8c850306b7205d985ee96c7b41076ac361844 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 14 May 2025 13:31:44 +0100 Subject: [PATCH 2/3] Merges load methods --- RATapi/outputs.py | 137 ++++++++++++++++++++---------------------- tests/test_outputs.py | 9 +-- 2 files changed, 67 insertions(+), 79 deletions(-) diff --git a/RATapi/outputs.py b/RATapi/outputs.py index afad575b..69edc239 100644 --- a/RATapi/outputs.py +++ b/RATapi/outputs.py @@ -258,7 +258,7 @@ def save(self, filepath: Union[str, Path] = "./results.json"): filepath.write_text(json.dumps(json_dict)) @classmethod - def load(cls, path: Union[str, Path]) -> "Results": + def load(cls, path: Union[str, Path]) -> Union["Results", "BayesResults"]: """Load a Results object from file. Parameters @@ -272,20 +272,45 @@ def load(cls, path: Union[str, Path]) -> "Results": results_dict = read_core_results_fields(results_dict) - return Results( - reflectivity=results_dict["reflectivity"], - simulation=results_dict["simulation"], - shiftedData=results_dict["shiftedData"], - backgrounds=results_dict["backgrounds"], - resolutions=results_dict["resolutions"], - sldProfiles=results_dict["sldProfiles"], - layers=results_dict["layers"], - resampledLayers=results_dict["resampledLayers"], - calculationResults=CalculationResults(**results_dict["calculationResults"]), - contrastParams=ContrastParams(**results_dict["contrastParams"]), - fitParams=np.array(results_dict["fitParams"]), - fitNames=results_dict["fitNames"], - ) + if all(key in results_dict for key in bayes_results_subclasses): + results_dict = read_bayes_results_fields(results_dict) + + return BayesResults( + reflectivity=results_dict["reflectivity"], + simulation=results_dict["simulation"], + shiftedData=results_dict["shiftedData"], + backgrounds=results_dict["backgrounds"], + resolutions=results_dict["resolutions"], + sldProfiles=results_dict["sldProfiles"], + layers=results_dict["layers"], + resampledLayers=results_dict["resampledLayers"], + calculationResults=CalculationResults(**results_dict["calculationResults"]), + contrastParams=ContrastParams(**results_dict["contrastParams"]), + fitParams=np.array(results_dict["fitParams"]), + fitNames=results_dict["fitNames"], + predictionIntervals=PredictionIntervals(**results_dict["predictionIntervals"]), + confidenceIntervals=ConfidenceIntervals(**results_dict["confidenceIntervals"]), + dreamParams=DreamParams(**results_dict["dreamParams"]), + dreamOutput=DreamOutput(**results_dict["dreamOutput"]), + nestedSamplerOutput=NestedSamplerOutput(**results_dict["nestedSamplerOutput"]), + chain=np.array(results_dict["chain"]), + ) + + else: + return Results( + reflectivity=results_dict["reflectivity"], + simulation=results_dict["simulation"], + shiftedData=results_dict["shiftedData"], + backgrounds=results_dict["backgrounds"], + resolutions=results_dict["resolutions"], + sldProfiles=results_dict["sldProfiles"], + layers=results_dict["layers"], + resampledLayers=results_dict["resampledLayers"], + calculationResults=CalculationResults(**results_dict["calculationResults"]), + contrastParams=ContrastParams(**results_dict["contrastParams"]), + fitParams=np.array(results_dict["fitParams"]), + fitNames=results_dict["fitNames"], + ) @dataclass @@ -548,63 +573,6 @@ def save(self, filepath: Union[str, Path] = "./results.json"): json_dict["chain"] = self.chain.tolist() filepath.write_text(json.dumps(json_dict)) - @classmethod - def load(cls, path: Union[str, Path]) -> "BayesResults": - """Load a BayesResults object from file. - - Parameters - ---------- - path : str or Path - The path to the results json file. - """ - path = Path(path) - input_data = path.read_text() - results_dict = json.loads(input_data) - - results_dict = read_core_results_fields(results_dict) - - # Take each of the subclasses in a BayesResults instance and convert to numpy arrays where necessary - for subclass_name in bayes_results_subclasses: - subclass_dict = {} - - for field in bayes_results_fields["param_fields"][subclass_name]: - subclass_dict[field] = results_dict[subclass_name][field] - - for field in bayes_results_fields["list_fields"][subclass_name]: - subclass_dict[field] = [np.array(result_array) for result_array in results_dict[subclass_name][field]] - - for field in bayes_results_fields["double_list_fields"][subclass_name]: - subclass_dict[field] = [ - [np.array(result_array) for result_array in inner_list] - for inner_list in results_dict[subclass_name][field] - ] - - for field in bayes_results_fields["array_fields"][subclass_name]: - subclass_dict[field] = np.array(results_dict[subclass_name][field]) - - results_dict[subclass_name] = subclass_dict - - return BayesResults( - reflectivity=results_dict["reflectivity"], - simulation=results_dict["simulation"], - shiftedData=results_dict["shiftedData"], - backgrounds=results_dict["backgrounds"], - resolutions=results_dict["resolutions"], - sldProfiles=results_dict["sldProfiles"], - layers=results_dict["layers"], - resampledLayers=results_dict["resampledLayers"], - calculationResults=CalculationResults(**results_dict["calculationResults"]), - contrastParams=ContrastParams(**results_dict["contrastParams"]), - fitParams=np.array(results_dict["fitParams"]), - fitNames=results_dict["fitNames"], - predictionIntervals=PredictionIntervals(**results_dict["predictionIntervals"]), - confidenceIntervals=ConfidenceIntervals(**results_dict["confidenceIntervals"]), - dreamParams=DreamParams(**results_dict["dreamParams"]), - dreamOutput=DreamOutput(**results_dict["dreamOutput"]), - nestedSamplerOutput=NestedSamplerOutput(**results_dict["nestedSamplerOutput"]), - chain=np.array(results_dict["chain"]), - ) - def write_core_results_fields(results: Union[Results, BayesResults], json_dict: Optional[dict] = None) -> dict: """Modify the values of the fields that appear in both Results and BayesResults when saving to a json file.""" @@ -651,6 +619,31 @@ def read_core_results_fields(results_dict: dict) -> dict: return results_dict +def read_bayes_results_fields(results_dict: dict) -> dict: + """Modify the values of the fields that appear only in BayesResults when loading a json file.""" + for subclass_name in bayes_results_subclasses: + subclass_dict = {} + + for field in bayes_results_fields["param_fields"][subclass_name]: + subclass_dict[field] = results_dict[subclass_name][field] + + for field in bayes_results_fields["list_fields"][subclass_name]: + subclass_dict[field] = [np.array(result_array) for result_array in results_dict[subclass_name][field]] + + for field in bayes_results_fields["double_list_fields"][subclass_name]: + subclass_dict[field] = [ + [np.array(result_array) for result_array in inner_list] + for inner_list in results_dict[subclass_name][field] + ] + + for field in bayes_results_fields["array_fields"][subclass_name]: + subclass_dict[field] = np.array(results_dict[subclass_name][field]) + + results_dict[subclass_name] = subclass_dict + + return results_dict + + def make_results( procedure: Procedures, output_results: RATapi.rat_core.OutputResult, diff --git a/tests/test_outputs.py b/tests/test_outputs.py index 5cd97fc4..0fce4697 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -208,13 +208,8 @@ def test_results_str(test_output_results, test_str, request) -> None: assert test_output_results.__str__() == test_str -@pytest.mark.parametrize( - ["result_class", "test_results"], - [ - (RATapi.Results, "reflectivity_calculation_results"), - (RATapi.BayesResults, "dream_results"), - ], -) +@pytest.mark.parametrize("result_class", [RATapi.Results, RATapi.BayesResults]) +@pytest.mark.parametrize("test_results", ["reflectivity_calculation_results", "dream_results"]) def test_save_load(result_class, test_results, request): """Test that saving and loading an output object returns the same object.""" test_results = request.getfixturevalue(test_results) From fe244c8f727830df859576268a547886b78a2d9f Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 14 May 2025 17:16:07 +0100 Subject: [PATCH 3/3] Addresses review comments --- RATapi/outputs.py | 43 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/RATapi/outputs.py b/RATapi/outputs.py index 69edc239..e4c54c34 100644 --- a/RATapi/outputs.py +++ b/RATapi/outputs.py @@ -575,7 +575,20 @@ def save(self, filepath: Union[str, Path] = "./results.json"): def write_core_results_fields(results: Union[Results, BayesResults], json_dict: Optional[dict] = None) -> dict: - """Modify the values of the fields that appear in both Results and BayesResults when saving to a json file.""" + """Modify the values of the fields that appear in both Results and BayesResults when saving to a json file. + + Parameters + ---------- + results: Union[Results, BayesResults] + The results or BayesResults object we are writing to json. + json_dict: Optional[dict] + The dictionary containing the json output. + + Returns + ------- + json_dict: dict + The output json dict updated with the fields that appear in both Results and BayesResults. + """ if json_dict is None: json_dict = {} @@ -602,7 +615,19 @@ def write_core_results_fields(results: Union[Results, BayesResults], json_dict: def read_core_results_fields(results_dict: dict) -> dict: - """Modify the values of the fields that appear in both Results and BayesResults when loading a json file.""" + """Modify the values of the fields that appear in both Results and BayesResults when loading a json file. + + Parameters + ---------- + results_dict: Optional[dict] + The dictionary containing the json input. + + Returns + ------- + results_dict: dict + The input json dict with the fields that appear in both Results and BayesResults converted to numpy arrays + where necessary. + """ for field in results_fields["list_fields"]: results_dict[field] = [np.array(result_array) for result_array in results_dict[field]] @@ -620,7 +645,19 @@ def read_core_results_fields(results_dict: dict) -> dict: def read_bayes_results_fields(results_dict: dict) -> dict: - """Modify the values of the fields that appear only in BayesResults when loading a json file.""" + """Modify the values of the fields that appear only in BayesResults when loading a json file. + + Parameters + ---------- + results_dict: Optional[dict] + The dictionary containing the json input. + + Returns + ------- + results_dict: dict + The input json dict with the fields that appear in both Results and BayesResults converted to numpy arrays + where necessary. + """ for subclass_name in bayes_results_subclasses: subclass_dict = {}