diff --git a/force_bdss/base_core_driver.py b/force_bdss/base_core_driver.py index ca884ccac2d6462b581f1db81b5b86adbd777b07..6b43b4cec5a09370f51edab719cf9e0cd5254f0d 100644 --- a/force_bdss/base_core_driver.py +++ b/force_bdss/base_core_driver.py @@ -7,6 +7,9 @@ from .bundle_registry_plugin import ( ) from .io.workflow_reader import WorkflowReader from .workspecs.workflow import Workflow +from .mco.parameters.mco_parameter_factory_registry import ( + MCOParameterFactoryRegistry) +from .mco.parameters.core_mco_parameters import all_core_factories class BaseCoreDriver(Plugin): @@ -14,15 +17,27 @@ class BaseCoreDriver(Plugin): or the evaluation. """ + #: The registry of the bundles. bundle_registry = Instance(BundleRegistryPlugin) + #: The registry of the MCO parameters + parameter_factory_registry = Instance(MCOParameterFactoryRegistry) + #: Deserialized content of the workflow file. workflow = Instance(Workflow) def _bundle_registry_default(self): return self.application.get_plugin(BUNDLE_REGISTRY_PLUGIN_ID) + def _parameter_factory_registry_default(self): + registry = MCOParameterFactoryRegistry() + for f in all_core_factories(): + registry.register(f) + + return registry + def _workflow_default(self): - reader = WorkflowReader(self.bundle_registry) + reader = WorkflowReader(self.bundle_registry, + self.parameter_factory_registry) with open(self.application.workflow_filepath) as f: return reader.read(f) diff --git a/force_bdss/cli/tests/test_execution.py b/force_bdss/cli/tests/test_execution.py index 80f3c878f91a050fa71b4ec04b81bf8e82486d2a..24bf92bcd5915c4b411baa68b6e2f4f6c914519c 100644 --- a/force_bdss/cli/tests/test_execution.py +++ b/force_bdss/cli/tests/test_execution.py @@ -28,6 +28,16 @@ class TestExecution(unittest.TestCase): out = subprocess.check_call(["force_bdss", "test_csv.json"]) self.assertEqual(out, 0) + def test_plain_invocation_evaluate(self): + with cd(fixtures.dirpath()): + proc = subprocess.Popen([ + "force_bdss", "--evaluate", "test_csv.json"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + proc.communicate(b"1") + retcode = proc.wait() + self.assertEqual(retcode, 0) + def test_unsupported_file_input(self): with cd(fixtures.dirpath()): with self.assertRaises(subprocess.CalledProcessError): diff --git a/force_bdss/core_plugins/dummy/dummy_dakota/dakota_communicator.py b/force_bdss/core_plugins/dummy/dummy_dakota/dakota_communicator.py index dfcc11349ec3a2db0093958f096267340f619966..9f9d4419447c36cd0ab23e9236fd5fcd3985274e 100644 --- a/force_bdss/core_plugins/dummy/dummy_dakota/dakota_communicator.py +++ b/force_bdss/core_plugins/dummy/dummy_dakota/dakota_communicator.py @@ -8,8 +8,12 @@ class DummyDakotaCommunicator(BaseMCOCommunicator): def receive_from_mco(self): data = sys.stdin.read() values = list(map(float, data.split())) + value_names = [p.value_name for p in self.model.parameters] + value_types = [p.value_type for p in self.model.parameters] + return DataSourceParameters( - value_types=["DUMMY"]*len(values), + value_names=value_names, + value_types=value_types, values=numpy.array(values) ) diff --git a/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py b/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py index e91edb5b3146f94c3b30cc2eb79ac0f7d70c3ba5..97f39a897aded790eb39f402cedee555de5ee458 100644 --- a/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py +++ b/force_bdss/core_plugins/dummy/dummy_dakota/dakota_optimizer.py @@ -1,14 +1,34 @@ import subprocess - import sys +import itertools +import collections from force_bdss.api import BaseMultiCriteriaOptimizer +def rotated_range(start, stop, starting_value): + r = list(range(start, stop)) + start_idx = r.index(starting_value) + d = collections.deque(r) + d.rotate(-start_idx) + return list(d) + + class DummyDakotaOptimizer(BaseMultiCriteriaOptimizer): def run(self): - print("Running dakota optimizer") - for initial_value in range(10): + parameters = self.model.parameters + + values = [] + for p in parameters: + values.append( + rotated_range(int(p.lower_bound), + int(p.upper_bound), + int(p.initial_value)) + ) + + value_iterator = itertools.product(*values) + + for value in value_iterator: ps = subprocess.Popen( [sys.argv[0], "--evaluate", @@ -16,5 +36,7 @@ class DummyDakotaOptimizer(BaseMultiCriteriaOptimizer): stdout=subprocess.PIPE, stdin=subprocess.PIPE) - out = ps.communicate("{}".format(initial_value).encode("utf-8")) - print("{}: {}".format(initial_value, out[0].decode("utf-8"))) + out = ps.communicate( + " ".join([str(v) for v in value]).encode("utf-8")) + print("{}: {}".format(" ".join([str(v) for v in value]), + out[0].decode("utf-8"))) diff --git a/force_bdss/core_plugins/dummy/dummy_dakota/tests/test_dakota_communicator.py b/force_bdss/core_plugins/dummy/dummy_dakota/tests/test_dakota_communicator.py new file mode 100644 index 0000000000000000000000000000000000000000..a17b59d97dd3e71bbda4c874568b0208eedcf7f7 --- /dev/null +++ b/force_bdss/core_plugins/dummy/dummy_dakota/tests/test_dakota_communicator.py @@ -0,0 +1,37 @@ +import unittest + +from force_bdss.bdss_application import BDSSApplication + +from force_bdss.core_plugins.dummy.dummy_dakota.dakota_bundle import ( + DummyDakotaBundle) +from force_bdss.data_sources.data_source_parameters import DataSourceParameters + +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory +from force_bdss.mco.parameters.core_mco_parameters import RangedMCOParameter + +try: + import mock +except ImportError: + from unittest import mock + + +class TestDakotaCommunicator(unittest.TestCase): + def test_receive_from_mco(self): + bundle = DummyDakotaBundle() + mock_application = mock.Mock(spec=BDSSApplication) + mock_parameter_factory = mock.Mock(spec=BaseMCOParameterFactory) + model = bundle.create_model() + model.parameters = [ + RangedMCOParameter(mock_parameter_factory) + ] + comm = bundle.create_communicator(mock_application, model) + + with mock.patch("sys.stdin") as stdin: + stdin.read.return_value = "1" + + data = comm.receive_from_mco() + self.assertIsInstance(data, DataSourceParameters) + self.assertEqual(len(data.value_names), 1) + self.assertEqual(len(data.value_types), 1) + self.assertEqual(len(data.values), 1) diff --git a/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py b/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py index 20004b3a933fd6eba0da31a6069277d930aa36ea..a94c6b9d4cd471d1fe87f6796b57b76fee373300 100644 --- a/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py +++ b/force_bdss/core_plugins/dummy/dummy_data_source/dummy_data_source.py @@ -6,6 +6,7 @@ class DummyDataSource(BaseDataSource): print(parameters) return DataSourceResult( originator=self, + value_names=parameters.value_names, value_types=parameters.value_types, values=parameters.values.reshape( parameters.values.shape + (1,))) diff --git a/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py b/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py index aeee46827562c20ecb61ca87f7cd5ef2cac9fc05..c0df8ae8bc4f5baaed2097fd4eed1cb489726572 100644 --- a/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py +++ b/force_bdss/core_plugins/dummy/dummy_kpi_calculator/dummy_kpi_calculator.py @@ -5,8 +5,11 @@ class DummyKPICalculator(BaseKPICalculator): id = bundle_id("enthought", "dummy_kpi_calculator") def run(self, data_source_results): - return KPICalculatorResult( + res = KPICalculatorResult( originator=self, + value_names=data_source_results[0].value_names, value_types=data_source_results[0].value_types, values=data_source_results[0].values.reshape( data_source_results[0].values.shape[:-1])) + + return res diff --git a/force_bdss/core_plugins/dummy_mco/dakota/dakota_communicator.py b/force_bdss/core_plugins/dummy_mco/dakota/dakota_communicator.py index ee349e806571d776d49a0dfc533bd53224931322..1605eba63e872ff85dae5df694d566b425c84550 100644 --- a/force_bdss/core_plugins/dummy_mco/dakota/dakota_communicator.py +++ b/force_bdss/core_plugins/dummy_mco/dakota/dakota_communicator.py @@ -8,13 +8,21 @@ class DakotaCommunicator(BaseMCOCommunicator): def receive_from_mco(self): data = sys.stdin.read() values = list(map(float, data.split())) - value_types = self.model.value_types - if len(values) != len(value_types): - raise ValueError("Length of provided data differs from the number " - "of expected types. {} {}".format(values, - value_types)) + + parameters = self.model.parameters + + if len(values) != len(parameters): + raise ValueError( + "The passed information length is {}, " + "but the model specifies {} values.".format( + len(values), len(parameters) + )) + + value_types = [p.value_type for p in parameters] + value_names = [p.value_name for p in parameters] return DataSourceParameters( + value_names=value_names, value_types=value_types, values=numpy.array(values) ) diff --git a/force_bdss/data_sources/data_source_parameters.py b/force_bdss/data_sources/data_source_parameters.py index 0eaf298b919088cde9b5d2991ae3ebfdd57cb9fd..ca2b5a4834965804c24d2ac6a96246d9c06d41dd 100644 --- a/force_bdss/data_sources/data_source_parameters.py +++ b/force_bdss/data_sources/data_source_parameters.py @@ -2,14 +2,19 @@ from traits.api import HasStrictTraits, Array, List, String class DataSourceParameters(HasStrictTraits): + value_names = List(String) value_types = List(String) values = Array(shape=(None,)) def __str__(self): return """ DataSourceParameters + value_names: + {} value_types: {} values: {} - """.format(str(self.value_types), str(self.values)) + """.format(str(self.value_names), + str(self.value_types), + str(self.values)) diff --git a/force_bdss/data_sources/data_source_result.py b/force_bdss/data_sources/data_source_result.py index fd19c48a45ffdc22d105cd3852d97beeb52df8a2..76bddc08eae5055d7f1359a436721567245bc9af 100644 --- a/force_bdss/data_sources/data_source_result.py +++ b/force_bdss/data_sources/data_source_result.py @@ -13,6 +13,7 @@ class DataSourceResult(HasTraits): the importance and reliability of that value. It should be an enumeration value such as HIGH, MEDIUM, POOR""" originator = Instance(BaseDataSource) + value_names = List(String) value_types = List(String) values = Array(shape=(None, None)) accuracy = ArrayOrNone(shape=(None, None)) @@ -25,6 +26,9 @@ class DataSourceResult(HasTraits): originator: {} + value_names: + {} + value_types: {} @@ -38,6 +42,7 @@ class DataSourceResult(HasTraits): {} """.format( self.originator, + self.value_names, self.value_types, self.values, self.accuracy, diff --git a/force_bdss/ids.py b/force_bdss/ids.py index 76b3016b09e4626fe7229cd8190d56b55aa13550..81ff1062170f547fc95cd90a79516781ad3848c6 100644 --- a/force_bdss/ids.py +++ b/force_bdss/ids.py @@ -25,6 +25,30 @@ def bundle_id(producer, identifier): A unique identifier for the bundle. The producer has authority and control over the uniqueness of this identifier. + Returns + ------- + str: an identifier to be used in the bundle. + """ + return _string_id("bundle", producer, identifier) + + +def mco_parameter_id(producer, identifier): + return _string_id("mco_parameter", producer, identifier) + + +def _string_id(entity_namespace, producer, identifier): + """Creates an id for a generic entity. + + Parameters + ---------- + entity_namespace: str + A namespace for the entity we want to address (e.g. "bundle") + producer: str + the company or research institute unique identifier (e.g. "enthought") + identifier: str + A unique identifier for the bundle. The producer has authority and + control over the uniqueness of this identifier. + Returns ------- str: an identifier to be used in the bundle. @@ -35,7 +59,7 @@ def bundle_id(producer, identifier): " " not in entry and len(entry) != 0) - if not all(map(is_valid, [producer, identifier])): + if not all(map(is_valid, [entity_namespace, producer, identifier])): raise ValueError("Invalid parameters specified.") - return "force.bdss.bundles.{}.{}".format(producer, identifier) + return "force.bdss.{}.{}.{}".format(entity_namespace, producer, identifier) diff --git a/force_bdss/io/tests/test_workflow_reader.py b/force_bdss/io/tests/test_workflow_reader.py index 53c73f4032d0802883d4cf8b039a311bfbccaeba..58518c5b4212655e406cc5315af38effc44ece4b 100644 --- a/force_bdss/io/tests/test_workflow_reader.py +++ b/force_bdss/io/tests/test_workflow_reader.py @@ -6,6 +6,8 @@ from force_bdss.bundle_registry_plugin import BundleRegistryPlugin from force_bdss.io.workflow_reader import ( WorkflowReader, InvalidVersionException, InvalidFileException) +from force_bdss.mco.parameters.mco_parameter_factory_registry import \ + MCOParameterFactoryRegistry try: import mock @@ -16,7 +18,12 @@ except ImportError: class TestWorkflowReader(unittest.TestCase): def setUp(self): self.mock_bundle_registry = mock.Mock(spec=BundleRegistryPlugin) - self.wfreader = WorkflowReader(self.mock_bundle_registry) + self.mock_mco_parameter_registry = mock.Mock( + spec=MCOParameterFactoryRegistry) + + self.wfreader = WorkflowReader( + self.mock_bundle_registry, + self.mock_mco_parameter_registry) def test_initialization(self): self.assertEqual(self.wfreader.bundle_registry, diff --git a/force_bdss/io/tests/test_workflow_writer.py b/force_bdss/io/tests/test_workflow_writer.py index 25103a0d0e80390342a7a83a2cb5ecf09c757159..88cd1c96c290d2c79cfb2556a1684d8baf57dfa5 100644 --- a/force_bdss/io/tests/test_workflow_writer.py +++ b/force_bdss/io/tests/test_workflow_writer.py @@ -4,13 +4,18 @@ from six import StringIO from force_bdss.bundle_registry_plugin import BundleRegistryPlugin from force_bdss.io.workflow_reader import WorkflowReader +from force_bdss.mco.parameters.base_mco_parameter import BaseMCOParameter +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory +from force_bdss.mco.parameters.mco_parameter_factory_registry import \ + MCOParameterFactoryRegistry try: import mock except ImportError: from unittest import mock -from force_bdss.ids import bundle_id +from force_bdss.ids import bundle_id, mco_parameter_id from force_bdss.io.workflow_writer import WorkflowWriter from force_bdss.mco.base_mco_model import BaseMCOModel from force_bdss.mco.i_multi_criteria_optimizer_bundle import \ @@ -33,6 +38,9 @@ class TestWorkflowWriter(unittest.TestCase): self.mock_registry.mco_bundle_by_id = mock.Mock( return_value=mock_mco_bundle) + self.mock_mco_parameter_registry = mock.Mock( + spec=MCOParameterFactoryRegistry) + def test_write(self): wfwriter = WorkflowWriter() fp = StringIO() @@ -51,7 +59,8 @@ class TestWorkflowWriter(unittest.TestCase): wf = self._create_mock_workflow() wfwriter.write(wf, fp) fp.seek(0) - wfreader = WorkflowReader(self.mock_registry) + wfreader = WorkflowReader(self.mock_registry, + self.mock_mco_parameter_registry) wf_result = wfreader.read(fp) self.assertEqual(wf_result.multi_criteria_optimizer.bundle.id, wf.multi_criteria_optimizer.bundle.id) @@ -62,4 +71,12 @@ class TestWorkflowWriter(unittest.TestCase): mock.Mock( spec=IMultiCriteriaOptimizerBundle, id=bundle_id("enthought", "mock"))) + wf.multi_criteria_optimizer.parameters = [ + BaseMCOParameter( + factory=mock.Mock( + spec=BaseMCOParameterFactory, + id=mco_parameter_id("enthought", "mock") + ) + ) + ] return wf diff --git a/force_bdss/io/workflow_reader.py b/force_bdss/io/workflow_reader.py index adead5f8e13f6214622b20ece5169b02921660f5..e713497892b4b72887e56c33d72979347781c101 100644 --- a/force_bdss/io/workflow_reader.py +++ b/force_bdss/io/workflow_reader.py @@ -3,8 +3,10 @@ import logging from traits.api import HasStrictTraits, Instance -from ..workspecs.workflow import Workflow +from ..mco.parameters.mco_parameter_factory_registry import ( + MCOParameterFactoryRegistry) from ..bundle_registry_plugin import BundleRegistryPlugin +from ..workspecs.workflow import Workflow SUPPORTED_FILE_VERSIONS = ["1"] @@ -26,7 +28,15 @@ class WorkflowReader(HasStrictTraits): #: bundle-specific model objects. bundle_registry = Instance(BundleRegistryPlugin) - def __init__(self, bundle_registry, *args, **kwargs): + #: The registry for the MCO parameters. At the moment this + #: is not extensible via plugins as the one above. + mco_parameter_registry = Instance(MCOParameterFactoryRegistry) + + def __init__(self, + bundle_registry, + mco_parameter_registry, + *args, + **kwargs): """Initializes the reader. Parameters @@ -36,6 +46,7 @@ class WorkflowReader(HasStrictTraits): for a bundle identified by a given id. """ self.bundle_registry = bundle_registry + self.mco_parameter_registry = mco_parameter_registry super(WorkflowReader, self).__init__(*args, **kwargs) @@ -115,8 +126,12 @@ class WorkflowReader(HasStrictTraits): mco_id = mco_data["id"] mco_bundle = registry.mco_bundle_by_id(mco_id) - return mco_bundle.create_model( + model_data = wf_data["multi_criteria_optimizer"]["model_data"] + model_data["parameters"] = self._extract_mco_parameters( + model_data["parameters"]) + model = mco_bundle.create_model( wf_data["multi_criteria_optimizer"]["model_data"]) + return model def _extract_data_sources(self, wf_data): """Extracts the data sources from the workflow dictionary data. @@ -166,3 +181,27 @@ class WorkflowReader(HasStrictTraits): kpic_bundle.create_model(kpic_entry["model_data"])) return kpi_calculators + + def _extract_mco_parameters(self, parameters_data): + """Extracts the MCO parameters from the data as dictionary. + + Parameters + ---------- + parameters_data: dict + The content of the parameter data key in the MCO model data. + + Returns + ------- + List of instances of a subclass of BaseMCOParameter + """ + registry = self.mco_parameter_registry + + parameters = [] + + for p in parameters_data: + id = p["id"] + factory = registry.get_factory_by_id(id) + model = factory.create_model(p["model_data"]) + parameters.append(model) + + return parameters diff --git a/force_bdss/io/workflow_writer.py b/force_bdss/io/workflow_writer.py index a83d54aab3c649a521a140d49774a89995275b8f..b757d039c7346b4a48db2371b84b018255ec6420 100644 --- a/force_bdss/io/workflow_writer.py +++ b/force_bdss/io/workflow_writer.py @@ -27,6 +27,18 @@ class WorkflowWriter(HasStrictTraits): "id": workflow.multi_criteria_optimizer.bundle.id, "model_data": workflow.multi_criteria_optimizer.__getstate__() } + + parameters_data = [] + for param in wf_data["multi_criteria_optimizer"]["model_data"]["parameters"]: # noqa + parameters_data.append( + { + "id": param.factory.id, + "model_data": param.__getstate__() + } + ) + + wf_data["multi_criteria_optimizer"]["model_data"]["parameters"] = parameters_data # noqa + kpic_data = [] for kpic in workflow.kpi_calculators: kpic_data.append({ diff --git a/force_bdss/kpi/kpi_calculator_result.py b/force_bdss/kpi/kpi_calculator_result.py index 4998c394c6a44aab52df2153e048d91e4b079fe8..237b8740c6dc71452b0c2905f407d899399f88c4 100644 --- a/force_bdss/kpi/kpi_calculator_result.py +++ b/force_bdss/kpi/kpi_calculator_result.py @@ -5,7 +5,37 @@ from .base_kpi_calculator import BaseKPICalculator class KPICalculatorResult(HasTraits): originator = Instance(BaseKPICalculator) + value_names = List(String) value_types = List(String) values = Array(shape=(None, )) accuracy = ArrayOrNone(shape=(None, )) quality = ArrayOrNone(shape=(None, )) + + def __str__(self): + return """ + DataSourceResults + + originator: + {} + + value_names: + {} + + value_types: + {} + + values: + {} + + Accuracy: + {} + + Quality: + {} + """.format( + self.originator, + self.value_names, + self.value_types, + self.values, + self.accuracy, + self.quality) diff --git a/force_bdss/mco/base_mco_model.py b/force_bdss/mco/base_mco_model.py index e7466353e63a32edc862d744b643752f6c2f88cb..d2d4e39c6fc7084a77a18e0978f81b8ad3e71b0b 100644 --- a/force_bdss/mco/base_mco_model.py +++ b/force_bdss/mco/base_mco_model.py @@ -1,5 +1,6 @@ -from traits.api import ABCHasStrictTraits, Instance +from traits.api import ABCHasStrictTraits, Instance, List +from .parameters.base_mco_parameter import BaseMCOParameter from .i_multi_criteria_optimizer_bundle import IMultiCriteriaOptimizerBundle @@ -17,6 +18,9 @@ class BaseMCOModel(ABCHasStrictTraits): visible=False, transient=True) + # A list of the parameters for the MCO + parameters = List(BaseMCOParameter) + def __init__(self, bundle, *args, **kwargs): self.bundle = bundle super(BaseMCOModel, self).__init__(*args, **kwargs) diff --git a/force_bdss/mco/parameters/__init__.py b/force_bdss/mco/parameters/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/force_bdss/mco/parameters/base_mco_parameter.py b/force_bdss/mco/parameters/base_mco_parameter.py new file mode 100644 index 0000000000000000000000000000000000000000..51f4503a98584ba78346329ebd54be3d684897b8 --- /dev/null +++ b/force_bdss/mco/parameters/base_mco_parameter.py @@ -0,0 +1,24 @@ +from traits.api import HasStrictTraits, String, Instance + +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory + + +class BaseMCOParameter(HasStrictTraits): + """The base class of all MCO Parameter models. + Must be reimplemented by specific classes handling the specific parameter + that MCOs understand. + """ + + #: The generating factory. Used to retrieve the ID at serialization. + factory = Instance(BaseMCOParameterFactory, visible=False, transient=True) + + #: A user defined name for the parameter + value_name = String() + + #: A CUBA key describing the type of the parameter + value_type = String() + + def __init__(self, factory, *args, **kwargs): + self.factory = factory + super(BaseMCOParameter, self).__init__(*args, **kwargs) diff --git a/force_bdss/mco/parameters/base_mco_parameter_factory.py b/force_bdss/mco/parameters/base_mco_parameter_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..61241739e6564816e3eab15ddbe6e5726463267b --- /dev/null +++ b/force_bdss/mco/parameters/base_mco_parameter_factory.py @@ -0,0 +1,29 @@ +from traits.has_traits import HasStrictTraits +from traits.trait_types import String, Type + + +class BaseMCOParameterFactory(HasStrictTraits): + """Factory that produces the model instance of a given BASEMCOParameter + instance. + + Must be reimplemented for the specific parameter.""" + + #: A unique string identifying the parameter + id = String() + + #: A user friendly name (for the UI) + name = String("Undefined parameter") + + #: A long description of the parameter + description = String("Undefined parameter") + + # The model class to instantiate when create_model is called. + model_class = Type('BaseMCOParameter') + + def create_model(self, data_values=None): + """Creates the instance of the model class and returns it. + """ + if data_values is None: + data_values = {} + + return self.model_class(factory=self, **data_values) diff --git a/force_bdss/mco/parameters/core_mco_parameters.py b/force_bdss/mco/parameters/core_mco_parameters.py new file mode 100644 index 0000000000000000000000000000000000000000..e33559151c8ac348d846bc19a55387249f242d37 --- /dev/null +++ b/force_bdss/mco/parameters/core_mco_parameters.py @@ -0,0 +1,33 @@ +from traits.api import Float + +from ...ids import mco_parameter_id +from .base_mco_parameter import BaseMCOParameter +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory + + +class RangedMCOParameter(BaseMCOParameter): + """Expresses a MCO parameter that has a range between two floating + point values.""" + initial_value = Float() + upper_bound = Float() + lower_bound = Float() + + +class RangedMCOParameterFactory(BaseMCOParameterFactory): + """The factory of the above model""" + id = mco_parameter_id("enthought", "ranged") + model_class = RangedMCOParameter + name = "Range" + description = "A ranged parameter in floating point values." + + +def all_core_factories(): + """Produces a list of all factories contained in this module.""" + import inspect + + return [c() + for c in inspect.getmodule(all_core_factories).__dict__.values() + if inspect.isclass(c) and + c is not BaseMCOParameterFactory and + issubclass(c, BaseMCOParameterFactory)] diff --git a/force_bdss/mco/parameters/mco_parameter_factory_registry.py b/force_bdss/mco/parameters/mco_parameter_factory_registry.py new file mode 100644 index 0000000000000000000000000000000000000000..709f7f9ecb0a39d787bf2f40d794ae373ff6728d --- /dev/null +++ b/force_bdss/mco/parameters/mco_parameter_factory_registry.py @@ -0,0 +1,21 @@ +from traits.api import HasStrictTraits, Dict, String + +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory + + +class MCOParameterFactoryRegistry(HasStrictTraits): + """Registry to keep the parameter factories and lookup them. + """ + # Temp: this will become an extension point. + factories = Dict(String, BaseMCOParameterFactory) + + def get_factory_by_id(self, id): + """Finds the factory by its id, so that we can obtain it as from + the id in the model file. + """ + return self.factories[id] + + def register(self, factory): + """Registers a new factory""" + self.factories[factory.id] = factory diff --git a/force_bdss/mco/parameters/tests/__init__.py b/force_bdss/mco/parameters/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/force_bdss/mco/parameters/tests/test_base_mco_parameter.py b/force_bdss/mco/parameters/tests/test_base_mco_parameter.py new file mode 100644 index 0000000000000000000000000000000000000000..d845ce04caaf19938241006cc007391c61ffceee --- /dev/null +++ b/force_bdss/mco/parameters/tests/test_base_mco_parameter.py @@ -0,0 +1,22 @@ +import unittest + +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory + +try: + import mock +except ImportError: + from unittest import mock + +from force_bdss.mco.parameters.base_mco_parameter import BaseMCOParameter + + +class DummyParameter(BaseMCOParameter): + pass + + +class TestBaseMCOParameter(unittest.TestCase): + def test_instantiation(self): + factory = mock.Mock(spec=BaseMCOParameterFactory) + param = DummyParameter(factory) + self.assertEqual(param.factory, factory) diff --git a/force_bdss/mco/parameters/tests/test_base_mco_parameter_factory.py b/force_bdss/mco/parameters/tests/test_base_mco_parameter_factory.py new file mode 100644 index 0000000000000000000000000000000000000000..3203aa718a8321e29b8d8a053d96f726886a1547 --- /dev/null +++ b/force_bdss/mco/parameters/tests/test_base_mco_parameter_factory.py @@ -0,0 +1,26 @@ +import unittest +from traits.api import Int + + +from force_bdss.mco.parameters.base_mco_parameter import BaseMCOParameter +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory + + +class DummyMCOParameter(BaseMCOParameter): + x = Int() + + +class DummyMCOParameterFactory(BaseMCOParameterFactory): + id = "foo" + name = "bar" + description = "baz" + model_class = DummyMCOParameter + + +class TestBaseMCOParameterFactory(unittest.TestCase): + def test_initialization(self): + factory = DummyMCOParameterFactory() + model = factory.create_model({"x": 42}) + self.assertIsInstance(model, DummyMCOParameter) + self.assertEqual(model.x, 42) diff --git a/force_bdss/mco/parameters/tests/test_core_mco_parameters.py b/force_bdss/mco/parameters/tests/test_core_mco_parameters.py new file mode 100644 index 0000000000000000000000000000000000000000..af6f4c96ab0b6536e44fd2d5465a7dd3934eb396 --- /dev/null +++ b/force_bdss/mco/parameters/tests/test_core_mco_parameters.py @@ -0,0 +1,14 @@ +import unittest + +from force_bdss.mco.parameters import core_mco_parameters +from force_bdss.mco.parameters.base_mco_parameter_factory import \ + BaseMCOParameterFactory + + +class TestCoreMCOParameters(unittest.TestCase): + def test_all_factories(self): + factories = core_mco_parameters.all_core_factories() + self.assertEqual(len(factories), 1) + + for f in factories: + self.assertIsInstance(f, BaseMCOParameterFactory) diff --git a/force_bdss/mco/parameters/tests/test_parameter_factory_registry.py b/force_bdss/mco/parameters/tests/test_parameter_factory_registry.py new file mode 100644 index 0000000000000000000000000000000000000000..2adbfc567ba55eb9fcfa30cc63882a1d076d2223 --- /dev/null +++ b/force_bdss/mco/parameters/tests/test_parameter_factory_registry.py @@ -0,0 +1,10 @@ +import unittest + +from force_bdss.mco.parameters.mco_parameter_factory_registry import \ + MCOParameterFactoryRegistry + + +class TestParameterFactoryRegistry(unittest.TestCase): + def test_registry_init(self): + reg = MCOParameterFactoryRegistry() + self.assertEqual(reg.factories, {}) diff --git a/force_bdss/tests/fixtures/test_csv.json b/force_bdss/tests/fixtures/test_csv.json index 1c15b0f07f2bdeee497b41ea655e31fb64b19dff..dbd1a9d9c205cfa1b8fdc9d59676a83f187b05a9 100644 --- a/force_bdss/tests/fixtures/test_csv.json +++ b/force_bdss/tests/fixtures/test_csv.json @@ -2,14 +2,20 @@ "version": "1", "workflow": { "multi_criteria_optimizer": { - "id": "force.bdss.bundles.enthought.dakota", + "id": "force.bdss.bundle.enthought.dakota", "model_data": { - "value_types": ["DUMMY"] + "parameters" : [ + { + "id": "force.bdss.mco_parameter.enthought.ranged", + "model_data": { + } + } + ] } }, "data_sources": [ { - "id": "force.bdss.bundles.enthought.csv_extractor", + "id": "force.bdss.bundle.enthought.csv_extractor", "model_data": { "filename": "foo.csv", "row": 3, @@ -18,7 +24,7 @@ } }, { - "id": "force.bdss.bundles.enthought.csv_extractor", + "id": "force.bdss.bundle.enthought.csv_extractor", "model_data": { "filename": "foo.csv", "row": 3, @@ -29,7 +35,7 @@ ], "kpi_calculators": [ { - "id": "force.bdss.bundles.enthought.kpi_adder", + "id": "force.bdss.bundle.enthought.kpi_adder", "model_data": { "cuba_type_in": "PRESSURE", "cuba_type_out": "TOTAL_PRESSURE" diff --git a/force_bdss/tests/test_id_generators.py b/force_bdss/tests/test_id_generators.py index 2317cf18a530a4f1c3d29e869472b3a40ee8c81a..3faefeb2b208e1808f66c80d3407792f7c042639 100644 --- a/force_bdss/tests/test_id_generators.py +++ b/force_bdss/tests/test_id_generators.py @@ -6,7 +6,7 @@ from force_bdss.ids import bundle_id class TestIdGenerators(unittest.TestCase): def test_bundle_id(self): self.assertEqual(bundle_id("foo", "bar"), - "force.bdss.bundles.foo.bar") + "force.bdss.bundle.foo.bar") for bad_entry in ["", None, " ", "foo bar"]: with self.assertRaises(ValueError):