Skip to content

Commit 0657f7d

Browse files
Implement passing constraints as arg
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.
1 parent 62b388d commit 0657f7d

File tree

6 files changed

+42
-36
lines changed

6 files changed

+42
-36
lines changed

environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ dependencies:
1818
- pytest
1919
- jacobi==0.4.2 #pinned until newer versions functionality confirmed
2020
- coverage
21+
- dill

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ dependencies = [
2626
"iminuit",
2727
"h5py",
2828
"jacobi==0.4.2",
29+
"dill",
2930
]
3031

3132
[project.optional-dependencies]

src/mvesuvio/analysis_reduction.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import numpy as np
22
import matplotlib.pyplot as plt
33
import scipy
4+
import dill # Only for converting constraints from string
45
from mantid.kernel import StringListValidator, Direction, IntArrayBoundedValidator, IntArrayProperty,\
56
IntBoundedValidator, FloatBoundedValidator
67
from mantid.api import FileProperty, FileAction, PythonAlgorithm, MatrixWorkspaceProperty
@@ -11,7 +12,7 @@
1112
VesuvioThickness, Integration, Divide, Multiply, DeleteWorkspaces, \
1213
CreateWorkspace, CreateSampleWorkspace
1314

14-
from mvesuvio.util.analysis_helpers import loadConstants, numericalThirdDerivative
15+
from mvesuvio.util.analysis_helpers import deserialize_lambdas, loadConstants, numericalThirdDerivative
1516

1617

1718

@@ -37,7 +38,7 @@ def PyInit(self):
3738
name="InputProfiles",
3839
defaultValue="",
3940
direction=Direction.Input),
40-
doc="Table workspace containing starting parameters for profiles"
41+
doc="Table workspace containing starting parameters for profiles."
4142
)
4243
self.declareProperty(FileProperty(
4344
name='InstrumentParametersFile',
@@ -61,17 +62,17 @@ def PyInit(self):
6162
name="InvalidDetectors",
6263
validator=IntArrayBoundedValidator(lower=3, upper=198),
6364
direction=Direction.Input),
64-
doc="List of invalid detectors whithin range 3-198"
65+
doc="List of invalid detectors whithin range 3-198."
6566
)
6667
self.declareProperty(
6768
name="MultipleScatteringCorrection",
6869
defaultValue=False,
69-
doc="Whether to run multiple scattering correction"
70+
doc="Whether to run multiple scattering correction."
7071
)
7172
self.declareProperty(
7273
name="GammaCorrection",
7374
defaultValue=False,
74-
doc="Whether to run gamma correction"
75+
doc="Whether to run gamma correction."
7576
)
7677
self.declareProperty(
7778
name="SampleVerticalWidth",
@@ -99,11 +100,11 @@ def PyInit(self):
99100
defaultValue="",
100101
doc="Directory where to save analysis results."
101102
)
102-
# self.declareProperty(
103-
# name="Constraints",
104-
# defaultValue=(),
105-
# doc="Constraints to use during fitting profiles."
106-
# )
103+
self.declareProperty(
104+
name="Constraints",
105+
defaultValue="",
106+
doc="Constraints to use during fitting profiles."
107+
)
107108
self.declareProperty(
108109
name="TransmissionGuess",
109110
defaultValue=-1.0,
@@ -122,12 +123,12 @@ def PyInit(self):
122123
self.declareProperty(
123124
name="ResultsPath",
124125
defaultValue="",
125-
doc="Directory to store results, to be deleted later"
126+
doc="Directory to store results, to be deleted later."
126127
)
127128
self.declareProperty(
128129
name="FiguresPath",
129130
defaultValue="",
130-
doc="Directory to store figures, to be deleted later"
131+
doc="Directory to store figures, to be deleted later."
131132
)
132133
# Outputs
133134
self.declareProperty(TableWorkspaceProperty(
@@ -158,7 +159,7 @@ def _setup(self):
158159
self._save_results_path = self.getProperty("ResultsPath").value
159160
self._save_figures_path = self.getProperty("FiguresPath").value
160161
self._h_ratio = self.getProperty("HRatioToLowestMass").value
161-
self._constraints = () #self.getProperty("Constraints").value
162+
self._constraints = dill.loads(eval(self.getProperty("Constraints").value))
162163

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

src/mvesuvio/analysis_routines.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
from mantid.simpleapi import CreateEmptyTableWorkspace, mtd, RenameWorkspace
44
from mantid.api import AlgorithmFactory, AlgorithmManager
55
import numpy as np
6+
import dill # To convert constraints to string
67

78
from mvesuvio.util.analysis_helpers import fix_profile_parameters, \
89
loadRawAndEmptyWsFromUserPath, cropAndMaskWorkspace, \
9-
NeutronComptonProfile, calculate_h_ratio
10+
NeutronComptonProfile, calculate_h_ratio, serialize_lambdas
1011
from mvesuvio.analysis_reduction import AnalysisRoutine
1112
from tests.testhelpers.calibration.algorithms import create_algorithm
1213

@@ -56,7 +57,7 @@ def _create_analysis_object_from_current_interface(IC, running_tests=False):
5657
"TransmissionGuess": IC.transmission_guess,
5758
"MultipleScatteringOrder": int(IC.multiple_scattering_order),
5859
"NumberOfEvents": int(IC.number_of_events),
59-
# Constraints"": IC.constraints,
60+
"Constraints": str(dill.dumps(IC.constraints)),
6061
"ResultsPath": str(IC.resultsSavePath),
6162
"FiguresPath": str(IC.figSavePath),
6263
"OutputMeansTable":" Final_Means"

tests/unit/analysis/test_analysis_helpers.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import unittest
22
import numpy as np
3+
import dill
34
import numpy.testing as nptest
45
from mock import MagicMock
56
from mvesuvio.util.analysis_helpers import extractWS, _convert_dict_to_table, \
@@ -109,10 +110,19 @@ def test_fix_profile_parameters_without_H(self):
109110

110111
def test_calculate_h_ratio(self):
111112
means_table_mock = MagicMock()
112-
means_table_mock.column.side_effect = lambda x: [16, 1, 12] if x is "mass" else [0.1, 0.85, 0.05]
113+
means_table_mock.column.side_effect = lambda x: [16, 1, 12] if x=="mass" else [0.1, 0.85, 0.05]
113114
h_ratio = calculate_h_ratio(means_table_mock)
114115
self.assertEqual(h_ratio, 0.85 / 0.05)
115116

116117

118+
def test_conversion_of_constraints(self):
119+
constraints = ({'type': 'eq', 'fun': lambda par: par[0] - 2.7527*par[3] },{'type': 'eq', 'fun': lambda par: par[3] - 0.7234*par[6] })
120+
string_constraints = str(dill.dumps(constraints))
121+
self.assertIsInstance(string_constraints, str)
122+
converted_constraints = dill.loads(eval(string_constraints))
123+
self.assertEqual(converted_constraints[0]['fun']([3, 0, 0, 1]), 3-2.7527)
124+
self.assertEqual(converted_constraints[1]['fun']([0, 0, 0, 2, 0, 0, 1]), 2-0.7234)
125+
126+
117127
if __name__ == "__main__":
118128
unittest.main()

tests/unit/analysis/test_analysis_reduction.py

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,25 @@
44
from mock import MagicMock
55
from mvesuvio.analysis_reduction import AnalysisRoutine
66
from mantid.simpleapi import CreateWorkspace, DeleteWorkspace
7+
import inspect
78

89

910
class TestAnalysisFunctions(unittest.TestCase):
1011
def setUp(self):
1112
pass
1213

13-
def test_calculate_h_ratio_masses_ordered(self):
14+
def test_constraints_are_passed_correctly(self):
1415
alg = AnalysisRoutine()
15-
alg._mean_intensity_ratios = np.array([0.91175, 0.06286, 0.00732, 0.01806])
16-
alg._masses = np.array([1.0079, 12.0, 16.0, 27.0])
17-
h_ratio = alg.calculate_h_ratio()
18-
self.assertAlmostEqual(14.504454343, h_ratio)
19-
20-
def test_calculate_h_ratio_masses_unordered(self):
21-
alg = AnalysisRoutine()
22-
alg._mean_intensity_ratios = np.array([0.00732, 0.06286, 0.01806, 0.91175])
23-
alg._masses = np.array([16.0, 12.0, 27.0, 1.0079])
24-
h_ratio = alg.calculate_h_ratio()
25-
self.assertAlmostEqual(14.504454343, h_ratio)
26-
27-
def test_calculate_h_ratio_hydrogen_missing(self):
28-
alg = AnalysisRoutine()
29-
alg._mean_intensity_ratios = np.array([0.00732, 0.06286, 0.01806])
30-
alg._masses = np.array([16.0, 12.0, 27.0])
31-
h_ratio = alg.calculate_h_ratio()
32-
self.assertAlmostEqual(None, h_ratio)
33-
16+
alg.initialize()
17+
constraints = (
18+
{'type': 'eq', 'fun': lambda par: par[0] - 2.7527*par[3] }, {'type': 'eq', 'fun': lambda par: par[3] - 0.7234*par[6] })
19+
for c in constraints:
20+
print(inspect.getsourcelines(c['fun'])[0])
21+
# alg.setProperty("Constraints", str(constraints))
22+
# print(str(constraints))
23+
# print(alg.getPropertyValue("Constraints"))
24+
# alg_constraints = eval(alg.getPropertyValue("Constraints"))
25+
# self.assertEqual(constraints, alg_constraints)
3426

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

0 commit comments

Comments
 (0)