Skip to content

Commit

Permalink
Implement passing constraints as arg
Browse files Browse the repository at this point in the history
Previously the constraints argument was disabled because
I did not find a way to pass Python objects (tuple of dictionaries) into
the mantid algorithm.

This workaround converts the tuple of dictionaries into a byte string
using the dill library which can then be converted to a normal string and
passed into the mantid algorithm as an argument.
When the algorithm executes, the string is converted into byte string again
though eval() and then into the tuple of dicts of lambdas by the dill library.

A better solution might be implemented in the future but this will suffice for now.
  • Loading branch information
GuiMacielPereira committed Sep 16, 2024
1 parent 62b388d commit 0657f7d
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 36 deletions.
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ dependencies:
- pytest
- jacobi==0.4.2 #pinned until newer versions functionality confirmed
- coverage
- dill
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies = [
"iminuit",
"h5py",
"jacobi==0.4.2",
"dill",
]

[project.optional-dependencies]
Expand Down
27 changes: 14 additions & 13 deletions src/mvesuvio/analysis_reduction.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import numpy as np
import matplotlib.pyplot as plt
import scipy
import dill # Only for converting constraints from string
from mantid.kernel import StringListValidator, Direction, IntArrayBoundedValidator, IntArrayProperty,\
IntBoundedValidator, FloatBoundedValidator
from mantid.api import FileProperty, FileAction, PythonAlgorithm, MatrixWorkspaceProperty
Expand All @@ -11,7 +12,7 @@
VesuvioThickness, Integration, Divide, Multiply, DeleteWorkspaces, \
CreateWorkspace, CreateSampleWorkspace

from mvesuvio.util.analysis_helpers import loadConstants, numericalThirdDerivative
from mvesuvio.util.analysis_helpers import deserialize_lambdas, loadConstants, numericalThirdDerivative



Expand All @@ -37,7 +38,7 @@ def PyInit(self):
name="InputProfiles",
defaultValue="",
direction=Direction.Input),
doc="Table workspace containing starting parameters for profiles"
doc="Table workspace containing starting parameters for profiles."
)
self.declareProperty(FileProperty(
name='InstrumentParametersFile',
Expand All @@ -61,17 +62,17 @@ def PyInit(self):
name="InvalidDetectors",
validator=IntArrayBoundedValidator(lower=3, upper=198),
direction=Direction.Input),
doc="List of invalid detectors whithin range 3-198"
doc="List of invalid detectors whithin range 3-198."
)
self.declareProperty(
name="MultipleScatteringCorrection",
defaultValue=False,
doc="Whether to run multiple scattering correction"
doc="Whether to run multiple scattering correction."
)
self.declareProperty(
name="GammaCorrection",
defaultValue=False,
doc="Whether to run gamma correction"
doc="Whether to run gamma correction."
)
self.declareProperty(
name="SampleVerticalWidth",
Expand Down Expand Up @@ -99,11 +100,11 @@ def PyInit(self):
defaultValue="",
doc="Directory where to save analysis results."
)
# self.declareProperty(
# name="Constraints",
# defaultValue=(),
# doc="Constraints to use during fitting profiles."
# )
self.declareProperty(
name="Constraints",
defaultValue="",
doc="Constraints to use during fitting profiles."
)
self.declareProperty(
name="TransmissionGuess",
defaultValue=-1.0,
Expand All @@ -122,12 +123,12 @@ def PyInit(self):
self.declareProperty(
name="ResultsPath",
defaultValue="",
doc="Directory to store results, to be deleted later"
doc="Directory to store results, to be deleted later."
)
self.declareProperty(
name="FiguresPath",
defaultValue="",
doc="Directory to store figures, to be deleted later"
doc="Directory to store figures, to be deleted later."
)
# Outputs
self.declareProperty(TableWorkspaceProperty(
Expand Down Expand Up @@ -158,7 +159,7 @@ def _setup(self):
self._save_results_path = self.getProperty("ResultsPath").value
self._save_figures_path = self.getProperty("FiguresPath").value
self._h_ratio = self.getProperty("HRatioToLowestMass").value
self._constraints = () #self.getProperty("Constraints").value
self._constraints = dill.loads(eval(self.getProperty("Constraints").value))

self._profiles_table = self.getProperty("InputProfiles").value

Expand Down
5 changes: 3 additions & 2 deletions src/mvesuvio/analysis_routines.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
from mantid.simpleapi import CreateEmptyTableWorkspace, mtd, RenameWorkspace
from mantid.api import AlgorithmFactory, AlgorithmManager
import numpy as np
import dill # To convert constraints to string

from mvesuvio.util.analysis_helpers import fix_profile_parameters, \
loadRawAndEmptyWsFromUserPath, cropAndMaskWorkspace, \
NeutronComptonProfile, calculate_h_ratio
NeutronComptonProfile, calculate_h_ratio, serialize_lambdas
from mvesuvio.analysis_reduction import AnalysisRoutine
from tests.testhelpers.calibration.algorithms import create_algorithm

Expand Down Expand Up @@ -56,7 +57,7 @@ def _create_analysis_object_from_current_interface(IC, running_tests=False):
"TransmissionGuess": IC.transmission_guess,
"MultipleScatteringOrder": int(IC.multiple_scattering_order),
"NumberOfEvents": int(IC.number_of_events),
# Constraints"": IC.constraints,
"Constraints": str(dill.dumps(IC.constraints)),
"ResultsPath": str(IC.resultsSavePath),
"FiguresPath": str(IC.figSavePath),
"OutputMeansTable":" Final_Means"
Expand Down
12 changes: 11 additions & 1 deletion tests/unit/analysis/test_analysis_helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest
import numpy as np
import dill
import numpy.testing as nptest
from mock import MagicMock
from mvesuvio.util.analysis_helpers import extractWS, _convert_dict_to_table, \
Expand Down Expand Up @@ -109,10 +110,19 @@ def test_fix_profile_parameters_without_H(self):

def test_calculate_h_ratio(self):
means_table_mock = MagicMock()
means_table_mock.column.side_effect = lambda x: [16, 1, 12] if x is "mass" else [0.1, 0.85, 0.05]
means_table_mock.column.side_effect = lambda x: [16, 1, 12] if x=="mass" else [0.1, 0.85, 0.05]
h_ratio = calculate_h_ratio(means_table_mock)
self.assertEqual(h_ratio, 0.85 / 0.05)


def test_conversion_of_constraints(self):
constraints = ({'type': 'eq', 'fun': lambda par: par[0] - 2.7527*par[3] },{'type': 'eq', 'fun': lambda par: par[3] - 0.7234*par[6] })
string_constraints = str(dill.dumps(constraints))
self.assertIsInstance(string_constraints, str)
converted_constraints = dill.loads(eval(string_constraints))
self.assertEqual(converted_constraints[0]['fun']([3, 0, 0, 1]), 3-2.7527)
self.assertEqual(converted_constraints[1]['fun']([0, 0, 0, 2, 0, 0, 1]), 2-0.7234)


if __name__ == "__main__":
unittest.main()
32 changes: 12 additions & 20 deletions tests/unit/analysis/test_analysis_reduction.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,25 @@
from mock import MagicMock
from mvesuvio.analysis_reduction import AnalysisRoutine
from mantid.simpleapi import CreateWorkspace, DeleteWorkspace
import inspect


class TestAnalysisFunctions(unittest.TestCase):
def setUp(self):
pass

def test_calculate_h_ratio_masses_ordered(self):
def test_constraints_are_passed_correctly(self):
alg = AnalysisRoutine()
alg._mean_intensity_ratios = np.array([0.91175, 0.06286, 0.00732, 0.01806])
alg._masses = np.array([1.0079, 12.0, 16.0, 27.0])
h_ratio = alg.calculate_h_ratio()
self.assertAlmostEqual(14.504454343, h_ratio)

def test_calculate_h_ratio_masses_unordered(self):
alg = AnalysisRoutine()
alg._mean_intensity_ratios = np.array([0.00732, 0.06286, 0.01806, 0.91175])
alg._masses = np.array([16.0, 12.0, 27.0, 1.0079])
h_ratio = alg.calculate_h_ratio()
self.assertAlmostEqual(14.504454343, h_ratio)

def test_calculate_h_ratio_hydrogen_missing(self):
alg = AnalysisRoutine()
alg._mean_intensity_ratios = np.array([0.00732, 0.06286, 0.01806])
alg._masses = np.array([16.0, 12.0, 27.0])
h_ratio = alg.calculate_h_ratio()
self.assertAlmostEqual(None, h_ratio)

alg.initialize()
constraints = (
{'type': 'eq', 'fun': lambda par: par[0] - 2.7527*par[3] }, {'type': 'eq', 'fun': lambda par: par[3] - 0.7234*par[6] })
for c in constraints:
print(inspect.getsourcelines(c['fun'])[0])
# alg.setProperty("Constraints", str(constraints))
# print(str(constraints))
# print(alg.getPropertyValue("Constraints"))
# alg_constraints = eval(alg.getPropertyValue("Constraints"))
# self.assertEqual(constraints, alg_constraints)

if __name__ == "__main__":
unittest.main()

0 comments on commit 0657f7d

Please sign in to comment.