From 85cb0282727b95662f7d73ef211ae1f0ce170038 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:42:07 +0000 Subject: [PATCH 1/2] Removes priors struct, adding relevant fields to the project --- .../DSPC_function_background.py | 1 + RATapi/inputs.py | 85 +++--- RATapi/run.py | 3 +- cpp/RAT | 2 +- cpp/rat.cpp | 268 +++++++----------- tests/test_inputs.py | 190 ++++++------- 6 files changed, 219 insertions(+), 330 deletions(-) diff --git a/RATapi/examples/normal_reflectivity/DSPC_function_background.py b/RATapi/examples/normal_reflectivity/DSPC_function_background.py index 8a691107..756a2b3e 100644 --- a/RATapi/examples/normal_reflectivity/DSPC_function_background.py +++ b/RATapi/examples/normal_reflectivity/DSPC_function_background.py @@ -145,6 +145,7 @@ def DSPC_function_background(): problem.custom_files.append( name="D2O Background Function", filename="background_function.py", + function_name="backgroundFunction", language="python", path=pathlib.Path(__file__).parent.resolve(), ) diff --git a/RATapi/inputs.py b/RATapi/inputs.py index e747e3ab..812b9ac4 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -10,9 +10,19 @@ import RATapi import RATapi.controls import RATapi.wrappers -from RATapi.rat_core import Checks, Control, Limits, NameStore, Priors, ProblemDefinition +from RATapi.rat_core import Checks, Control, Limits, NameStore, ProblemDefinition from RATapi.utils.enums import Calculations, Languages, LayerModels, TypeOptions +parameter_field = { + "parameters": "params", + "bulk_in": "bulkIns", + "bulk_out": "bulkOuts", + "scalefactors": "scalefactors", + "domain_ratios": "domainRatios", + "background_parameters": "backgroundParams", + "resolution_parameters": "resolutionParams", +} + def get_python_handle(file_name: str, function_name: str, path: Union[str, pathlib.Path] = "") -> Callable: """Get the function handle from a function defined in a python module located anywhere within the filesystem. @@ -94,7 +104,7 @@ def __len__(self): return len(self.files) -def make_input(project: RATapi.Project, controls: RATapi.Controls) -> tuple[ProblemDefinition, Limits, Priors, Control]: +def make_input(project: RATapi.Project, controls: RATapi.Controls) -> tuple[ProblemDefinition, Limits, Control]: """Constructs the inputs required for the compiled RAT code using the data defined in the input project and controls. @@ -111,65 +121,32 @@ def make_input(project: RATapi.Project, controls: RATapi.Controls) -> tuple[Prob The problem input used in the compiled RAT code. limits : RAT.rat_core.Limits A list of min/max values for each parameter defined in the project. - priors : RAT.rat_core.Priors - The priors defined for each parameter in the project. cpp_controls : RAT.rat_core.Control The controls object used in the compiled RAT code. """ - parameter_field = { - "parameters": "params", - "bulk_in": "bulkIns", - "bulk_out": "bulkOuts", - "scalefactors": "scalefactors", - "domain_ratios": "domainRatios", - "background_parameters": "backgroundParams", - "resolution_parameters": "resolutionParams", - } - - prior_id = {"uniform": 1, "gaussian": 2, "jeffreys": 3} - - checks = Checks() limits = Limits() - priors = Priors() for class_list in RATapi.project.parameter_class_lists: - setattr(checks, parameter_field[class_list], [int(element.fit) for element in getattr(project, class_list)]) setattr( limits, parameter_field[class_list], [[element.min, element.max] for element in getattr(project, class_list)], ) - setattr( - priors, - parameter_field[class_list], - [[element.name, element.prior_type, element.mu, element.sigma] for element in getattr(project, class_list)], - ) - # Use dummy values for qzshifts - checks.qzshifts = [] + # Use dummy value for qzshifts limits.qzshifts = [] - priors.qzshifts = [] - - priors.priorNames = [ - param.name for class_list in RATapi.project.parameter_class_lists for param in getattr(project, class_list) - ] - priors.priorValues = [ - [prior_id[param.prior_type], param.mu, param.sigma] - for class_list in RATapi.project.parameter_class_lists - for param in getattr(project, class_list) - ] if project.model == LayerModels.CustomXY: controls.calcSldDuringFit = True - problem = make_problem(project, checks) + problem = make_problem(project) cpp_controls = make_controls(controls) - return problem, limits, priors, cpp_controls + return problem, limits, cpp_controls -def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: +def make_problem(project: RATapi.Project) -> ProblemDefinition: """Constructs the problem input required for the compiled RAT code. Parameters @@ -184,6 +161,7 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: """ hydrate_id = {"bulk in": 1, "bulk out": 2} + prior_id = {"uniform": 1, "gaussian": 2, "jeffreys": 3} # Set contrast parameters according to model type if project.model == LayerModels.StandardLayers: @@ -384,21 +362,32 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: for param in getattr(project, class_list) if not param.fit ] + problem.priorNames = [ + param.name for class_list in RATapi.project.parameter_class_lists for param in getattr(project, class_list) + ] + problem.priorValues = [ + [prior_id[param.prior_type], param.mu, param.sigma] + for class_list in RATapi.project.parameter_class_lists + for param in getattr(project, class_list) + ] # Names problem.names = NameStore() - problem.names.params = [param.name for param in project.parameters] - problem.names.backgroundParams = [param.name for param in project.background_parameters] - problem.names.scalefactors = [param.name for param in project.scalefactors] - problem.names.qzshifts = [] # Placeholder for qzshifts - problem.names.bulkIns = [param.name for param in project.bulk_in] - problem.names.bulkOuts = [param.name for param in project.bulk_out] - problem.names.resolutionParams = [param.name for param in project.resolution_parameters] - problem.names.domainRatios = [param.name for param in project.domain_ratios] + for class_list in RATapi.project.parameter_class_lists: + setattr(problem.names, parameter_field[class_list], [param.name for param in getattr(project, class_list)]) problem.names.contrasts = [contrast.name for contrast in project.contrasts] # Checks - problem.checks = checks + problem.checks = Checks() + for class_list in RATapi.project.parameter_class_lists: + setattr( + problem.checks, parameter_field[class_list], [int(element.fit) for element in getattr(project, class_list)] + ) + + # Use dummy values for qz shifts + problem.names.qzshifts = [] + problem.checks.qzshifts = [] + check_indices(problem) return problem diff --git a/RATapi/run.py b/RATapi/run.py index d60b76e0..6b158255 100644 --- a/RATapi/run.py +++ b/RATapi/run.py @@ -104,7 +104,7 @@ def run(project, controls): horizontal_line = "\u2500" * 107 + "\n" display_on = controls.display != Display.Off - problem_definition, limits, priors, cpp_controls = make_input(project, controls) + problem_definition, limits, cpp_controls = make_input(project, controls) if display_on: print("Starting RAT " + horizontal_line) @@ -115,7 +115,6 @@ def run(project, controls): problem_definition, limits, cpp_controls, - priors, ) end = time.time() diff --git a/cpp/RAT b/cpp/RAT index e14ea44c..b46dd2d1 160000 --- a/cpp/RAT +++ b/cpp/RAT @@ -1 +1 @@ -Subproject commit e14ea44c57b376e9f73936320b0d460e8bec3779 +Subproject commit b46dd2d1e5c76669ee980f88144314c851aee3e4 diff --git a/cpp/rat.cpp b/cpp/rat.cpp index 759e7d48..f159927f 100644 --- a/cpp/rat.cpp +++ b/cpp/rat.cpp @@ -395,20 +395,6 @@ struct BayesResults py::array_t chain; }; -struct Priors -{ - py::list params; - py::list backgroundParams; - py::list scalefactors; - py::list qzshifts; - py::list bulkIns; - py::list bulkOuts; - py::list resolutionParams; - py::list domainRatios; - py::list priorNames; - py::array_t priorValues; -}; - struct Checks { py::array_t params; py::array_t backgroundParams; @@ -515,6 +501,8 @@ struct ProblemDefinition { py::array_t otherParams; py::array_t fitLimits; py::array_t otherLimits; + py::list priorNames; + py::array_t priorValues; NameStore names; Checks checks {}; }; @@ -665,47 +653,6 @@ coder::array pyArrayToRatArray2d(py::array_t value) return result; } -coder::array pyListToUnboundedCell0(py::list values) -{ - coder::array result; - result.set_size(values.size()); - int32_T idx {0}; - for (py::handle list: values) - { - py::list value = py::cast(list); - if (py::len(list) != 4 || !py::isinstance(value[0]) || !py::isinstance(value[1]) || - !py::isinstance(value[2]) || !py::isinstance(value[3])) - throw std::runtime_error("Expects a 2D list where each row must contain 4 elements. " - "Columns 1 and 2 must be strings and Columns 3 and 4 must be numeric arrays"); - - stringToRatBoundedArray(value[0].cast(), result[idx].f1.data, result[idx].f1.size); - stringToRatBoundedArray(value[1].cast(), result[idx].f2.data, result[idx].f2.size); - result[idx].f3 = value[2].cast(); - result[idx].f4 = value[3].cast(); - idx++; - } - - return result; -} - -coder::array pyListToUnboundedCell1(py::list values) -{ - coder::array result; - result.set_size(values.size()); - int32_T idx {0}; - for (py::handle list: values) - { - if (py::isinstance(list)) { - std::string value = py::cast(list); - stringToRatCharArray(value, result[idx].f1); - idx++; - } - else - throw std::runtime_error("Expects a 1D list of strings"); - } - - return result; -} coder::array pyListToRatCellWrap1(py::list values) { coder::array result; @@ -800,7 +747,26 @@ coder::array pyListToRatCellWrap6(py::list values) return result; } -coder::array pyListToRatCellWrap0(py::list values) +coder::array pyListToRatCellWrap01d(py::list values) +{ + coder::array result; + result.set_size(values.size()); + int32_T idx {0}; + for (py::handle array: values) + { + if (py::isinstance(array)) { + std::string name = py::cast(array); + stringToRatBoundedArray(name, result[idx].f1.data, result[idx].f1.size); + idx++; + } + else + throw std::runtime_error("Expects a 1D list of strings"); + } + + return result; +} + +coder::array pyListToRatCellWrap02d(py::list values) { coder::array result; result.set_size(1, values.size()); @@ -839,15 +805,15 @@ coder::array py_function_array_to_rat_cell_wrap_0(py::obje RAT::struct1_T createStruct1(const NameStore& names) { RAT::struct1_T names_struct; - names_struct.params = customCaller("NameStore.params", pyListToRatCellWrap0, names.params); - names_struct.backgroundParams = customCaller("NameStore.backgroundParams", pyListToRatCellWrap0, names.backgroundParams); - names_struct.scalefactors = customCaller("NameStore.scalefactors", pyListToRatCellWrap0, names.scalefactors); - names_struct.qzshifts = customCaller("NameStore.qzshifts", pyListToRatCellWrap0, names.qzshifts); - names_struct.bulkIns = customCaller("NameStore.bulkIns", pyListToRatCellWrap0, names.bulkIns); - names_struct.bulkOuts = customCaller("NameStore.bulkOuts", pyListToRatCellWrap0, names.bulkOuts); - names_struct.resolutionParams = customCaller("NameStore.resolutionParams", pyListToRatCellWrap0, names.resolutionParams); - names_struct.domainRatios = customCaller("NameStore.domainRatios", pyListToRatCellWrap0, names.domainRatios); - names_struct.contrasts = customCaller("NameStore.contrasts", pyListToRatCellWrap0, names.contrasts); + names_struct.params = customCaller("NameStore.params", pyListToRatCellWrap02d, names.params); + names_struct.backgroundParams = customCaller("NameStore.backgroundParams", pyListToRatCellWrap02d, names.backgroundParams); + names_struct.scalefactors = customCaller("NameStore.scalefactors", pyListToRatCellWrap02d, names.scalefactors); + names_struct.qzshifts = customCaller("NameStore.qzshifts", pyListToRatCellWrap02d, names.qzshifts); + names_struct.bulkIns = customCaller("NameStore.bulkIns", pyListToRatCellWrap02d, names.bulkIns); + names_struct.bulkOuts = customCaller("NameStore.bulkOuts", pyListToRatCellWrap02d, names.bulkOuts); + names_struct.resolutionParams = customCaller("NameStore.resolutionParams", pyListToRatCellWrap02d, names.resolutionParams); + names_struct.domainRatios = customCaller("NameStore.domainRatios", pyListToRatCellWrap02d, names.domainRatios); + names_struct.contrasts = customCaller("NameStore.contrasts", pyListToRatCellWrap02d, names.contrasts); return names_struct; } @@ -883,14 +849,14 @@ RAT::struct0_T createStruct0(const ProblemDefinition& problem) problem_struct.useImaginary = problem.useImaginary; problem_struct.repeatLayers = customCaller("Problem.repeatLayers", pyListToRatCellWrap2, problem.repeatLayers); problem_struct.contrastBackgroundParams = customCaller("Problem.contrastBackgroundParams", pyListToRatCellWrap3, problem.contrastBackgroundParams); - problem_struct.contrastBackgroundTypes = customCaller("Problem.contrastBackgroundTypes", pyListToRatCellWrap0, problem.contrastBackgroundTypes); - problem_struct.contrastBackgroundActions = customCaller("Problem.contrastBackgroundActions", pyListToRatCellWrap0, problem.contrastBackgroundActions); + problem_struct.contrastBackgroundTypes = customCaller("Problem.contrastBackgroundTypes", pyListToRatCellWrap02d, problem.contrastBackgroundTypes); + problem_struct.contrastBackgroundActions = customCaller("Problem.contrastBackgroundActions", pyListToRatCellWrap02d, problem.contrastBackgroundActions); problem_struct.contrastQzshifts = customCaller("Problem.contrastQzshifts", pyArrayToRatRowArray1d, problem.contrastQzshifts); problem_struct.contrastScalefactors = customCaller("Problem.contrastScalefactors", pyArrayToRatRowArray1d, problem.contrastScalefactors); problem_struct.contrastBulkIns = customCaller("Problem.contrastBulkIns", pyArrayToRatRowArray1d, problem.contrastBulkIns); problem_struct.contrastBulkOuts = customCaller("Problem.contrastBulkOuts", pyArrayToRatRowArray1d, problem.contrastBulkOuts); problem_struct.contrastResolutionParams = customCaller("Problem.contrastResolutionParams", pyListToRatCellWrap4, problem.contrastResolutionParams); - problem_struct.contrastResolutionTypes = customCaller("Problem.contrastResolutionTypes", pyListToRatCellWrap0, problem.contrastResolutionTypes); + problem_struct.contrastResolutionTypes = customCaller("Problem.contrastResolutionTypes", pyListToRatCellWrap02d, problem.contrastResolutionTypes); problem_struct.backgroundParams = customCaller("Problem.backgroundParams", pyArrayToRatRowArray1d, problem.backgroundParams); problem_struct.qzshifts = customCaller("Problem.qzshifts", pyArrayToRatRowArray1d, problem.qzshifts); problem_struct.scalefactors = customCaller("Problem.scalefactors", pyArrayToRatRowArray1d, problem.scalefactors); @@ -912,6 +878,8 @@ RAT::struct0_T createStruct0(const ProblemDefinition& problem) problem_struct.otherParams = customCaller("Problem.otherParams", pyArrayToRatRowArray1d, problem.otherParams); problem_struct.fitLimits = customCaller("Problem.fitLimits", pyArrayToRatArray2d, problem.fitLimits); problem_struct.otherLimits = customCaller("Problem.otherLimits", pyArrayToRatArray2d, problem.otherLimits); + problem_struct.priorNames = customCaller("Problem.priorNames", pyListToRatCellWrap01d, problem.priorNames); + problem_struct.priorValues = customCaller("Problem.priorValues", pyArrayToRatArray2d, problem.priorValues); problem_struct.names = createStruct1(problem.names); problem_struct.checks = createStruct2(problem.checks); @@ -936,22 +904,6 @@ RAT::struct3_T createStruct3(const Limits& limits) return limits_struct; } -RAT::struct5_T createStruct5(const Priors& priors) -{ - RAT::struct5_T priors_struct; - priors_struct.params = customCaller("Priors.params", pyListToUnboundedCell0, priors.params); - priors_struct.backgroundParams = customCaller("Priors.backgroundParams", pyListToUnboundedCell0, priors.backgroundParams); - priors_struct.scalefactors = customCaller("Priors.scalefactors", pyListToUnboundedCell0, priors.scalefactors); - priors_struct.qzshifts = customCaller("Priors.qzshifts", pyListToUnboundedCell0, priors.qzshifts); - priors_struct.bulkIns = customCaller("Priors.bulkIns", pyListToUnboundedCell0, priors.bulkIns); - priors_struct.bulkOuts = customCaller("Priors.bulkOuts", pyListToUnboundedCell0, priors.bulkOuts); - priors_struct.resolutionParams = customCaller("Priors.resolutionParams", pyListToUnboundedCell0, priors.resolutionParams); - priors_struct.domainRatios = customCaller("Priors.domainRatios", pyListToUnboundedCell0, priors.domainRatios); - priors_struct.priorNames = customCaller("Priors.priorNames", pyListToUnboundedCell1, priors.priorNames); - priors_struct.priorValues = customCaller("Priors.priorValues", pyArrayToRatArray2d, priors.priorValues); - - return priors_struct; -} RAT::struct4_T createStruct4(const Control& control) { @@ -1008,7 +960,19 @@ py::array_t pyArrayFromRatArray2d(coder::array array) return result_array; } -py::list pyListFromRatCellWrap0(coder::array values) +py::list pyListFromRatCellWrap01d(coder::array values) +{ + py::list result; + for (int32_T idx0{0}; idx0 < values.size(0); idx0++) { + std::string tmp; + stringFromRatBoundedArray(values[idx0].f1.data, values[idx0].f1.size, tmp); + result.append(tmp); + } + + return result; +} + +py::list pyListFromRatCellWrap02d(coder::array values) { py::list result; for (int32_T idx0{0}; idx0 < values.size(1); idx0++) { @@ -1114,7 +1078,7 @@ py::array_t pyArrayFromRatArray3d(coder::array array) return result_array; } -OutputResult OutputResultFromStruct6T(const RAT::struct6_T result) +OutputResult OutputResultFromStruct5T(const RAT::struct5_T result) { // Copy problem to output OutputResult output_result; @@ -1240,14 +1204,14 @@ ProblemDefinition problemDefinitionFromStruct0T(const RAT::struct0_T problem) problem_def.useImaginary = problem.useImaginary; problem_def.repeatLayers = pyListFromRatCellWrap2(problem.repeatLayers); problem_def.contrastBackgroundParams = pyListFromBoundedCellWrap>(problem.contrastBackgroundParams); - problem_def.contrastBackgroundTypes = pyListFromRatCellWrap0(problem.contrastBackgroundTypes); - problem_def.contrastBackgroundActions = pyListFromRatCellWrap0(problem.contrastBackgroundActions); + problem_def.contrastBackgroundTypes = pyListFromRatCellWrap02d(problem.contrastBackgroundTypes); + problem_def.contrastBackgroundActions = pyListFromRatCellWrap02d(problem.contrastBackgroundActions); problem_def.contrastQzshifts = pyArrayFromRatArray1d>(problem.contrastQzshifts); problem_def.contrastScalefactors = pyArrayFromRatArray1d>(problem.contrastScalefactors); problem_def.contrastBulkIns = pyArrayFromRatArray1d>(problem.contrastBulkIns); problem_def.contrastBulkOuts = pyArrayFromRatArray1d>(problem.contrastBulkOuts); problem_def.contrastResolutionParams = pyListFromBoundedCellWrap>(problem.contrastResolutionParams); - problem_def.contrastResolutionTypes = pyListFromRatCellWrap0(problem.contrastResolutionTypes); + problem_def.contrastResolutionTypes = pyListFromRatCellWrap02d(problem.contrastResolutionTypes); problem_def.backgroundParams = pyArrayFromRatArray1d>(problem.backgroundParams); problem_def.qzshifts = pyArrayFromRatArray1d>(problem.qzshifts); problem_def.scalefactors = pyArrayFromRatArray1d>(problem.scalefactors); @@ -1269,16 +1233,18 @@ ProblemDefinition problemDefinitionFromStruct0T(const RAT::struct0_T problem) problem_def.otherParams = pyArrayFromRatArray1d>(problem.otherParams); problem_def.fitLimits = pyArrayFromRatArray2d(problem.fitLimits); problem_def.otherLimits = pyArrayFromRatArray2d(problem.otherLimits); + problem_def.priorNames = pyListFromRatCellWrap01d(problem.priorNames); + problem_def.priorValues = pyArrayFromRatArray2d(problem.priorValues); - problem_def.names.params = pyListFromRatCellWrap0(problem.names.params); - problem_def.names.backgroundParams = pyListFromRatCellWrap0(problem.names.backgroundParams); - problem_def.names.scalefactors = pyListFromRatCellWrap0(problem.names.scalefactors); - problem_def.names.qzshifts = pyListFromRatCellWrap0(problem.names.qzshifts); - problem_def.names.bulkIns = pyListFromRatCellWrap0(problem.names.bulkIns); - problem_def.names.bulkOuts = pyListFromRatCellWrap0(problem.names.bulkOuts); - problem_def.names.resolutionParams = pyListFromRatCellWrap0(problem.names.resolutionParams); - problem_def.names.domainRatios = pyListFromRatCellWrap0(problem.names.domainRatios); - problem_def.names.contrasts = pyListFromRatCellWrap0(problem.names.contrasts); + problem_def.names.params = pyListFromRatCellWrap02d(problem.names.params); + problem_def.names.backgroundParams = pyListFromRatCellWrap02d(problem.names.backgroundParams); + problem_def.names.scalefactors = pyListFromRatCellWrap02d(problem.names.scalefactors); + problem_def.names.qzshifts = pyListFromRatCellWrap02d(problem.names.qzshifts); + problem_def.names.bulkIns = pyListFromRatCellWrap02d(problem.names.bulkIns); + problem_def.names.bulkOuts = pyListFromRatCellWrap02d(problem.names.bulkOuts); + problem_def.names.resolutionParams = pyListFromRatCellWrap02d(problem.names.resolutionParams); + problem_def.names.domainRatios = pyListFromRatCellWrap02d(problem.names.domainRatios); + problem_def.names.contrasts = pyListFromRatCellWrap02d(problem.names.contrasts); problem_def.checks.params = pyArrayFromRatArray1d>(problem.checks.params); problem_def.checks.backgroundParams = pyArrayFromRatArray1d>(problem.checks.backgroundParams); @@ -1292,14 +1258,14 @@ ProblemDefinition problemDefinitionFromStruct0T(const RAT::struct0_T problem) return problem_def; } -BayesResults bayesResultsFromStruct9T(const RAT::struct9_T results) +BayesResults bayesResultsFromStruct8T(const RAT::struct8_T results) { BayesResults bayesResults; bayesResults.chain = pyArrayFromRatArray2d(results.chain); - bayesResults.predictionIntervals.reflectivity = pyList1DFromRatCellWrap1D>(results.predictionIntervals.reflectivity); - bayesResults.predictionIntervals.sld = pyList2dFromRatCellWrap>(results.predictionIntervals.sld); + bayesResults.predictionIntervals.reflectivity = pyList1DFromRatCellWrap1D>(results.predictionIntervals.reflectivity); + bayesResults.predictionIntervals.sld = pyList2dFromRatCellWrap>(results.predictionIntervals.sld); bayesResults.predictionIntervals.sampleChi = pyArray1dFromBoundedArray>(results.predictionIntervals.sampleChi); bayesResults.confidenceIntervals.percentile95 = pyArrayFromRatArray2d(results.confidenceIntervals.percentile95); @@ -1343,23 +1309,22 @@ BayesResults bayesResultsFromStruct9T(const RAT::struct9_T results) return bayesResults; } -py::tuple RATMain(const ProblemDefinition& problem_def, const Limits& limits, const Control& control, const Priors& priors) +py::tuple RATMain(const ProblemDefinition& problem_def, const Limits& limits, const Control& control) { RAT::struct0_T problem_def_struct = createStruct0(problem_def); RAT::struct3_T limits_struct = createStruct3(limits); RAT::struct4_T control_struct = createStruct4(control); - RAT::struct5_T priors_struct = createStruct5(priors); // Output - RAT::struct6_T results; - RAT::struct9_T bayesResults; + RAT::struct5_T results; + RAT::struct8_T bayesResults; // Call the entry-point - RAT::RATMain(&problem_def_struct, &limits_struct, &control_struct, &priors_struct, &results, &bayesResults); + RAT::RATMain(&problem_def_struct, &limits_struct, &control_struct, &results, &bayesResults); // Copy result to output auto out_problem_def = problemDefinitionFromStruct0T(problem_def_struct); out_problem_def.customFiles = problem_def.customFiles.attr("copy")(); return py::make_tuple(out_problem_def, - OutputResultFromStruct6T(results), - bayesResultsFromStruct9T(bayesResults)); + OutputResultFromStruct5T(results), + bayesResultsFromStruct8T(bayesResults)); } py::array_t makeSLDProfileXY(real_T bulk_in, @@ -1677,45 +1642,6 @@ PYBIND11_MODULE(rat_core, m) { return lim; })); - py::class_(m, "Priors") - .def(py::init<>()) - .def_readwrite("params", &Priors::params) - .def_readwrite("backgroundParams", &Priors::backgroundParams) - .def_readwrite("scalefactors", &Priors::scalefactors) - .def_readwrite("qzshifts", &Priors::qzshifts) - .def_readwrite("bulkIns", &Priors::bulkIns) - .def_readwrite("bulkOuts", &Priors::bulkOuts) - .def_readwrite("resolutionParams", &Priors::resolutionParams) - .def_readwrite("domainRatios", &Priors::domainRatios) - .def_readwrite("priorNames", &Priors::priorNames) - .def_readwrite("priorValues", &Priors::priorValues) - .def(py::pickle( - [](const Priors &prior) { // __getstate__ - /* Return a tuple that fully encodes the state of the object */ - return py::make_tuple(prior.params, prior.backgroundParams, prior.scalefactors, prior.qzshifts, prior.bulkIns, - prior.bulkOuts, prior.resolutionParams, prior.domainRatios, prior.priorNames, prior.priorValues); - }, - [](py::tuple t) { // __setstate__ - if (t.size() != 10) - throw std::runtime_error("Encountered invalid state unpickling Limits object!"); - - /* Create a new C++ instance */ - Priors prior; - - prior.params = t[0].cast(); - prior.backgroundParams = t[1].cast(); - prior.scalefactors = t[2].cast(); - prior.qzshifts = t[3].cast(); - prior.bulkIns = t[4].cast(); - prior.bulkOuts = t[5].cast(); - prior.resolutionParams = t[6].cast(); - prior.domainRatios = t[7].cast(); - prior.priorNames = t[8].cast(); - prior.priorValues = t[9].cast>(); - - return prior; - })); - py::class_(m, "Control") .def(py::init<>()) .def_readwrite("parallel", &Control::parallel) @@ -1840,6 +1766,8 @@ PYBIND11_MODULE(rat_core, m) { .def_readwrite("otherParams", &ProblemDefinition::otherParams) .def_readwrite("fitLimits", &ProblemDefinition::fitLimits) .def_readwrite("otherLimits", &ProblemDefinition::otherLimits) + .def_readwrite("priorNames", &ProblemDefinition::priorNames) + .def_readwrite("priorValues", &ProblemDefinition::priorValues) .def_readwrite("names", &ProblemDefinition::names) .def_readwrite("checks", &ProblemDefinition::checks) .def(py::pickle( @@ -1853,13 +1781,13 @@ PYBIND11_MODULE(rat_core, m) { p.bulkIns, p.bulkOuts, p.resolutionParams, p.params, p.numberOfLayers, p.contrastLayers, p.layersDetails, p.customFiles, p.modelType, p.contrastCustomFiles, p.contrastDomainRatios, p.domainRatios, p.numberOfDomainContrasts, p.domainContrastLayers, p.fitParams, p.otherParams, p.fitLimits, - p.otherLimits, p.names.params, p.names.backgroundParams, p.names.scalefactors, p.names.qzshifts, - p.names.bulkIns, p.names.bulkOuts, p.names.resolutionParams, p.names.domainRatios, p.names.contrasts, - p.checks.params, p.checks.backgroundParams, p.checks.scalefactors, p.checks.qzshifts, + p.otherLimits, p.priorNames, p.priorValues, p.names.params, p.names.backgroundParams, p.names.scalefactors, + p.names.qzshifts, p.names.bulkIns, p.names.bulkOuts, p.names.resolutionParams, p.names.domainRatios, + p.names.contrasts, p.checks.params, p.checks.backgroundParams, p.checks.scalefactors, p.checks.qzshifts, p.checks.bulkIns, p.checks.bulkOuts, p.checks.resolutionParams, p.checks.domainRatios); }, [](py::tuple t) { // __setstate__ - if (t.size() != 58) + if (t.size() != 60) throw std::runtime_error("Encountered invalid state unpickling ProblemDefinition object!"); /* Create a new C++ instance */ @@ -1906,25 +1834,27 @@ PYBIND11_MODULE(rat_core, m) { p.otherParams = t[38].cast>(); p.fitLimits = t[39].cast>(); p.otherLimits = t[40].cast>(); + p.priorNames = t[41].cast(); + p.priorValues = t[42].cast>(); - p.names.params = t[41].cast(); - p.names.backgroundParams = t[42].cast(); - p.names.scalefactors = t[43].cast(); - p.names.qzshifts = t[44].cast(); - p.names.bulkIns = t[45].cast(); - p.names.bulkOuts = t[46].cast(); - p.names.resolutionParams = t[47].cast(); - p.names.domainRatios = t[48].cast(); - p.names.contrasts = t[49].cast(); - - p.checks.params = t[50].cast>(); - p.checks.backgroundParams = t[51].cast>(); - p.checks.scalefactors = t[52].cast>(); - p.checks.qzshifts = t[53].cast>(); - p.checks.bulkIns = t[54].cast>(); - p.checks.bulkOuts = t[55].cast>(); - p.checks.resolutionParams = t[56].cast>(); - p.checks.domainRatios = t[57].cast>(); + p.names.params = t[43].cast(); + p.names.backgroundParams = t[44].cast(); + p.names.scalefactors = t[45].cast(); + p.names.qzshifts = t[46].cast(); + p.names.bulkIns = t[47].cast(); + p.names.bulkOuts = t[48].cast(); + p.names.resolutionParams = t[49].cast(); + p.names.domainRatios = t[50].cast(); + p.names.contrasts = t[51].cast(); + + p.checks.params = t[52].cast>(); + p.checks.backgroundParams = t[53].cast>(); + p.checks.scalefactors = t[54].cast>(); + p.checks.qzshifts = t[55].cast>(); + p.checks.bulkIns = t[56].cast>(); + p.checks.bulkOuts = t[57].cast>(); + p.checks.resolutionParams = t[58].cast>(); + p.checks.domainRatios = t[59].cast>(); return p; })); diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 32fc4835..f9bf3ae8 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -9,7 +9,7 @@ import RATapi import RATapi.wrappers from RATapi.inputs import FileHandles, check_indices, make_controls, make_input, make_problem -from RATapi.rat_core import Checks, Control, Limits, NameStore, Priors, ProblemDefinition +from RATapi.rat_core import Checks, Control, Limits, NameStore, ProblemDefinition from RATapi.utils.enums import ( BackgroundActions, BoundHandling, @@ -197,6 +197,28 @@ def standard_layers_problem(test_names, test_checks): [6.2e-06, 6.35e-06], [0.01, 0.05], ] + problem.priorNames = [ + "Substrate Roughness", + "Test Thickness", + "Test SLD", + "Test Roughness", + "Background Param 1", + "Scalefactor 1", + "SLD Air", + "SLD D2O", + "Resolution Param 1", + ] + problem.priorValues = [ + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + ] problem.customFiles = FileHandles([]) problem.names = test_names problem.checks = test_checks @@ -258,6 +280,30 @@ def domains_problem(test_names, test_checks): [0.01, 0.05], [0.4, 0.6], ] + problem.priorNames = [ + "Substrate Roughness", + "Test Thickness", + "Test SLD", + "Test Roughness", + "Background Param 1", + "Scalefactor 1", + "SLD Air", + "SLD D2O", + "Resolution Param 1", + "Domain Ratio 1", + ] + problem.priorValues = [ + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + ] problem.customFiles = FileHandles([]) problem.names = test_names problem.names.domainRatios = ["Domain Ratio 1"] @@ -319,6 +365,28 @@ def custom_xy_problem(test_names, test_checks): [6.2e-06, 6.35e-06], [0.01, 0.05], ] + problem.priorNames = [ + "Substrate Roughness", + "Test Thickness", + "Test SLD", + "Test Roughness", + "Background Param 1", + "Scalefactor 1", + "SLD Air", + "SLD D2O", + "Resolution Param 1", + ] + problem.priorValues = [ + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + ] problem.customFiles = FileHandles( [RATapi.models.CustomFile(name="Test Custom File", filename="cpp_test.dll", language="cpp")] ) @@ -360,94 +428,6 @@ def domains_limits(): return limits -@pytest.fixture -def normal_priors(): - """The expected priors object from "standard_layers_project" and "custom_xy_project".""" - priors = Priors() - priors.params = [ - ["Substrate Roughness", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Thickness", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test SLD", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Roughness", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ] - priors.backgroundParams = [["Background Param 1", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.scalefactors = [["Scalefactor 1", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.qzshifts = [] - priors.bulkIns = [["SLD Air", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.bulkOuts = [["SLD D2O", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.resolutionParams = [["Resolution Param 1", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.domainRatios = [] - priors.priorNames = [ - "Substrate Roughness", - "Test Thickness", - "Test SLD", - "Test Roughness", - "Background Param 1", - "Scalefactor 1", - "SLD Air", - "SLD D2O", - "Resolution Param 1", - ] - priors.priorValues = [ - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - ] - - return priors - - -@pytest.fixture -def domains_priors(): - """The expected priors object from "domains_project".""" - priors = Priors() - priors.params = [ - ["Substrate Roughness", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Thickness", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test SLD", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Roughness", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf], - ] - priors.backgroundParams = [["Background Param 1", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.scalefactors = [["Scalefactor 1", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.qzshifts = [] - priors.bulkIns = [["SLD Air", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.bulkOuts = [["SLD D2O", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.resolutionParams = [["Resolution Param 1", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.domainRatios = [["Domain Ratio 1", RATapi.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.priorNames = [ - "Substrate Roughness", - "Test Thickness", - "Test SLD", - "Test Roughness", - "Background Param 1", - "Scalefactor 1", - "SLD Air", - "SLD D2O", - "Resolution Param 1", - "Domain Ratio 1", - ] - priors.priorValues = [ - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - [1, 0.0, np.inf], - ] - - return priors - - @pytest.fixture def standard_layers_controls(): """The expected controls object for input to the compiled RAT code given the default inputs and @@ -525,39 +505,35 @@ def custom_xy_controls(): @pytest.mark.parametrize( - ["test_project", "test_problem", "test_limits", "test_priors", "test_controls"], + ["test_project", "test_problem", "test_limits", "test_controls"], [ ( "standard_layers_project", "standard_layers_problem", "normal_limits", - "normal_priors", "standard_layers_controls", ), ( "custom_xy_project", "custom_xy_problem", "normal_limits", - "normal_priors", "custom_xy_controls", ), ( "domains_project", "domains_problem", "domains_limits", - "domains_priors", "standard_layers_controls", ), ], ) -def test_make_input(test_project, test_problem, test_limits, test_priors, test_controls, request) -> None: +def test_make_input(test_project, test_problem, test_limits, test_controls, request) -> None: """When converting the "project" and "controls", we should obtain the five input objects required for the compiled RAT code. """ test_project = request.getfixturevalue(test_project) test_problem = request.getfixturevalue(test_problem) test_limits = request.getfixturevalue(test_limits) - test_priors = request.getfixturevalue(test_priors) test_controls = request.getfixturevalue(test_controls) parameter_fields = [ @@ -571,7 +547,7 @@ def test_make_input(test_project, test_problem, test_limits, test_priors, test_c "domainRatios", ] - problem, limits, priors, controls = make_input(test_project, RATapi.Controls()) + problem, limits, controls = make_input(test_project, RATapi.Controls()) problem = pickle.loads(pickle.dumps(problem)) check_problem_equal(problem, test_problem) @@ -579,32 +555,24 @@ def test_make_input(test_project, test_problem, test_limits, test_priors, test_c for limit_field in parameter_fields: assert np.all(getattr(limits, limit_field) == getattr(test_limits, limit_field)) - priors = pickle.loads(pickle.dumps(priors)) - for prior_field in parameter_fields: - assert getattr(priors, prior_field) == getattr(test_priors, prior_field) - - assert priors.priorNames == test_priors.priorNames - assert np.all(priors.priorValues == test_priors.priorValues) - controls = pickle.loads(pickle.dumps(controls)) check_controls_equal(controls, test_controls) @pytest.mark.parametrize( - ["test_project", "test_check", "test_problem"], + ["test_project", "test_problem"], [ - ("standard_layers_project", "test_checks", "standard_layers_problem"), - ("custom_xy_project", "test_checks", "custom_xy_problem"), - ("domains_project", "test_checks", "domains_problem"), + ("standard_layers_project", "standard_layers_problem"), + ("custom_xy_project", "custom_xy_problem"), + ("domains_project", "domains_problem"), ], ) -def test_make_problem(test_project, test_problem, test_check, request) -> None: +def test_make_problem(test_project, test_problem, request) -> None: """The problem object should contain the relevant parameters defined in the input project object.""" test_project = request.getfixturevalue(test_project) test_problem = request.getfixturevalue(test_problem) - test_check = request.getfixturevalue(test_check) - problem = make_problem(test_project, test_check) + problem = make_problem(test_project) check_problem_equal(problem, test_problem) @@ -749,6 +717,7 @@ def check_problem_equal(actual_problem, expected_problem) -> None: "numberOfContrasts", "numberOfLayers", "numberOfDomainContrasts", + "priorNames", ] array_fields = [ @@ -780,6 +749,7 @@ def check_problem_equal(actual_problem, expected_problem) -> None: "otherParams", "fitLimits", "otherLimits", + "priorValues", ] checks_fields = [ "params", From 8e158df1027d4fec237c5141e5ea74bbaae609e7 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Fri, 21 Feb 2025 10:36:49 +0000 Subject: [PATCH 2/2] Addresses review comments --- RATapi/examples/normal_reflectivity/DSPC_function_background.py | 1 - RATapi/examples/normal_reflectivity/background_function.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/RATapi/examples/normal_reflectivity/DSPC_function_background.py b/RATapi/examples/normal_reflectivity/DSPC_function_background.py index 756a2b3e..8a691107 100644 --- a/RATapi/examples/normal_reflectivity/DSPC_function_background.py +++ b/RATapi/examples/normal_reflectivity/DSPC_function_background.py @@ -145,7 +145,6 @@ def DSPC_function_background(): problem.custom_files.append( name="D2O Background Function", filename="background_function.py", - function_name="backgroundFunction", language="python", path=pathlib.Path(__file__).parent.resolve(), ) diff --git a/RATapi/examples/normal_reflectivity/background_function.py b/RATapi/examples/normal_reflectivity/background_function.py index 6728d6f8..68993814 100644 --- a/RATapi/examples/normal_reflectivity/background_function.py +++ b/RATapi/examples/normal_reflectivity/background_function.py @@ -1,7 +1,7 @@ import numpy as np -def backgroundFunction(xdata, params): +def background_function(xdata, params): # Split up the params array Ao = params[0] k = params[1]