Пример #1
0
    def test_unit_handling(self):
        """
        Tests unit handling with a simple model that calculates the area of a rectangle as the
        product of two lengths.

        In this case the input lengths are provided in centimeters and meters.
        Tests whether the input units are properly coerced into canonical types.
        Tests whether the output units are properly set.
        Tests whether the model returns as predicted.
        Returns:
            None
        """
        L = Symbol('l', ['L'], ['L'], units=[1.0, [['centimeter', 1.0]]], shape=[1])
        A = Symbol('a', ['A'], ['A'], units=[1.0, [['centimeter', 2.0]]], shape=[1])
        get_area_config = {
            'name': 'area',
            # 'connections': [{'inputs': ['l1', 'l2'], 'outputs': ['a']}],
            'equations': ['a = l1 * l2'],
            # 'unit_map': {'l1': "cm", "l2": "cm", 'a': "cm^2"}
            'symbol_property_map': {"a": A, "l1": L, "l2": L}
        }
        model = EquationModel(**get_area_config)
        out = model.evaluate({'l1': QuantityFactory.create_quantity(L, 1, 'meter'),
                              'l2': QuantityFactory.create_quantity(L, 2)}, allow_failure=False)

        self.assertTrue(math.isclose(out['a'].magnitude, 200.0))
        self.assertTrue(out['a'].units == A.units)
Пример #2
0
    def test_model_returns_nan(self):
        # This tests model failure with scalar nan.
        # Quantity class has other more thorough tests.

        A = Symbol('a', ['A'], ['A'], units='dimensionless', shape=1)
        B = Symbol('b', ['B'], ['B'], units='dimensionless', shape=1)
        for sym in (B, A):
            Registry("symbols")[sym] = sym
            Registry("units")[sym] = sym.units
        get_config = {
            'name': 'equality',
            # 'connections': [{'inputs': ['b'], 'outputs': ['a']}],
            'equations': ['a = b'],
            # 'unit_map': {'a': "dimensionless", 'a': "dimensionless"}
            'variable_symbol_map': {
                "a": A,
                "b": B
            }
        }
        model = EquationModel(**get_config)
        out = model.evaluate(
            {'b': QuantityFactory.create_quantity(B, float('nan'))},
            allow_failure=True)
        self.assertFalse(out['successful'])
        self.assertEqual(out['message'],
                         'Evaluation returned invalid values (NaN)')
Пример #3
0
def validate(args):
    """Validates test data"""
    if args.name is not None:
        model = DEFAULT_MODEL_DICT[args.name]
        if not args.test_data:
            test_wrapper(model.validate_from_preset_test, pdb)
            print("Model validated with test data")
            return True
    elif args.file is not None:
        if args.file.endswith(".yaml"):
            EquationModel.from_file(args.file)
        elif args.file.endswith(".py"):
            # This should define config
            with open(args.file) as this_file:
                code = compile(this_file.read(), args.file, 'exec')
                exec(code, globals())
            config = globals().get('config')
            model = PyModel(**config)

    if args.test_data is not None:
        td_data = loadfn(args.test_data)
        for td_datum in td_data:
            test_wrapper(model.test, args.pdb, **td_datum)
        print("{} validated with test data".format(model.name))
    return True
Пример #4
0
    def test_model_returns_complex(self):
        # This tests model failure with scalar complex.
        # Quantity class has other more thorough tests.

        A = Symbol('a', ['A'], ['A'], units='dimensionless', shape=1)
        B = Symbol('b', ['B'], ['B'], units='dimensionless', shape=1)
        get_config = {
            'name': 'add_complex_value',
            # 'connections': [{'inputs': ['b'], 'outputs': ['a']}],
            'equations': ['a = b + 1j'],
            # 'unit_map': {'a': "dimensionless", 'a': "dimensionless"}
            'symbol_property_map': {
                "a": A,
                "b": B
            }
        }
        model = EquationModel(**get_config)
        out = model.evaluate({'b': Quantity(B, 5)}, allow_failure=True)
        self.assertFalse(out['successful'])
        self.assertEqual(out['message'],
                         'Evaluation returned invalid values (complex)')

        out = model.evaluate({'b': Quantity(B, 5j)}, allow_failure=True)
        self.assertTrue(out['successful'])
        self.assertTrue(np.isclose(out['a'].magnitude, 6j))
Пример #5
0
    def test_symbol_expansion_cyclic_constraints(self):
        """
        Tests the Symbol Expansion algorithm on a cyclic graph with constraints.
        The canonical graph and the canonical material are used for this test.
        """
        model4 = EquationModel("model4", ['D=B*C*11'], constraints=["G==0"])
        symbols = GraphTest.generate_canonical_symbols()
        models = GraphTest.generate_canonical_models(constrain_model_4=True)
        models['model4'] = model4
        material = GraphTest.generate_canonical_material(symbols)
        g = Graph(symbol_types=symbols, models=models, composite_models=dict())

        ts = []
        ans = []

        ts.append(g.calculable_properties({symbols['A']}))
        ans.append({x for x in symbols.values() if x is not symbols['A']})

        ts.append(g.calculable_properties({symbols['B']}))
        ans.append({symbols['F']})

        ts.append(g.calculable_properties({symbols['C']}))
        ans.append(set())

        ts.append(g.calculable_properties({symbols['C'], symbols['G']}))
        ans.append({symbols['D']})

        ts.append(g.calculable_properties({symbols['B'], symbols['C']}))
        ans.append({symbols['F']})

        for i in range(0, len(ts)):
            self.assertEqual(ts[i], ans[i],
                             "Symbol Expansion failed: test - " + str(i))
Пример #6
0
    def generate_canonical_models(constrain_model_4=False):
        """
        Returns a set of Model objects used in testing.
        Returns: (dict<str, Model>)
        """
        sym_map = GraphTest.generate_canonical_symbols()
        # TODO: Resolve the connections issue here
        model1 = EquationModel(name="model1", equations=['B=2*A', 'C=3*A'],
                               connections=[{"inputs": ["A"], "outputs": ['B', 'C']}],
                               symbol_property_map=sym_map)
        model2 = EquationModel(name="model2", equations=['G=5*A'],
                               symbol_property_map=sym_map)
        model3 = EquationModel(name="model3", equations=['F=7*B'],
                               symbol_property_map=sym_map)
        model5 = EquationModel(name="model5", equations=['D=C*G*13'],
                               symbol_property_map=sym_map)
        model6 = EquationModel(name="model6", equations=['A=F*D*17'],
                               symbol_property_map=sym_map)

        if constrain_model_4:
            model4 = EquationModel(name="model4", equations=['D=B*C*11'],
                                   constraints=["G==0"], symbol_property_map=sym_map)
        else:
            model4 = EquationModel(name="model4", equations=['D=B*C*11'],
                                   symbol_property_map=sym_map)

        models = [model1, model2, model3, model4, model5, model6]
        return {x.name : x for x in models}
Пример #7
0
def add_builtin_models_to_registry(register_symbols=True):
    _EQUATION_MODEL_NAMES_LIST.clear()
    # Load equation models
    equation_model_dir = os.path.join(os.path.dirname(__file__))
    equation_module_files = glob(equation_model_dir + '/*.yaml')

    if register_symbols:
        from propnet.symbols import add_builtin_symbols_to_registry
        add_builtin_symbols_to_registry()
    for filename in equation_module_files:
        model_path = os.path.join(equation_model_dir, filename)
        model = EquationModel.from_file(model_path,
                                        is_builtin=True,
                                        overwrite_registry=True)
        globals()[model.name] = model
        _EQUATION_MODEL_NAMES_LIST.append(model.name)
Пример #8
0
    def test_evaluate_constraints(self):
        """
        Tests the evaluation algorithm on a non-cyclic graph involving
        constraints.  The canonical graph and the canonical material are
        used for this test.
        """
        model4 = EquationModel(name="model4",
                               equations=["D=B*C*11"],
                               constraints=["G==0"])

        symbols = GraphTest.generate_canonical_symbols()
        models = GraphTest.generate_canonical_models()
        models['model4'] = model4
        del models['model6']
        material = GraphTest.generate_canonical_material(symbols)
        g = Graph(symbol_types=symbols, models=models, composite_models=dict())
        material_derived = g.evaluate(material)

        expected_quantities = [
            Quantity(symbols['A'], 19),
            Quantity(symbols['A'], 23),
            Quantity(symbols['B'], 38),
            Quantity(symbols['B'], 46),
            Quantity(symbols['C'], 57),
            Quantity(symbols['C'], 69),
            Quantity(symbols['G'], 95),
            Quantity(symbols['G'], 115),
            Quantity(symbols['F'], 266),
            Quantity(symbols['F'], 322),
            Quantity(symbols['D'], 70395),
            Quantity(symbols['D'], 85215),
            Quantity(symbols['D'], 103155)
        ]

        self.assertTrue(
            material == GraphTest.generate_canonical_material(symbols),
            "evaluate() mutated the original material argument.")

        derived_quantities = material_derived.get_quantities()
        self.assertTrue(
            len(expected_quantities) == len(derived_quantities),
            "Evaluate did not correctly derive outputs.")
        for q in expected_quantities:
            self.assertTrue(
                q in material_derived._symbol_to_quantity[q.symbol],
                "Evaluate failed to derive all outputs.")
            self.assertTrue(q in derived_quantities)
Пример #9
0
    def test_get_path_constraint(self):
        """
        Tests the ability to generate all paths from one symbol to another with constraints.
        """
        model4 = EquationModel("model4", ['D=B*C*11'], constraints=["G==0"])
        symbols = GraphTest.generate_canonical_symbols()
        models = GraphTest.generate_canonical_models(constrain_model_4=True)
        models['model4'] = model4
        del models['model6']
        g = Graph(symbol_types=symbols, models=models, composite_models=dict())

        paths_1 = g.get_paths(symbols['A'], symbols['F'])
        paths_2 = g.get_paths(symbols['A'], symbols['D'])

        ans_1 = [
            SymbolPath({symbols['A']}, [models['model1'], models['model3']])
        ]

        ans_2 = [
            SymbolPath({symbols['A'], symbols['C']},
                       [models['model2'], models['model5']]),
            SymbolPath({symbols['A'], symbols['G']},
                       [models['model1'], models['model5']]),
            SymbolPath({symbols['A'], symbols['C'], symbols['B']},
                       [models['model2'], models['model4']]),
            SymbolPath({symbols['A'], symbols['G']},
                       [models['model1'], models['model4']]),
            SymbolPath({symbols['A']},
                       [models['model1'], models['model2'], models['model5']]),
            SymbolPath({symbols['A']},
                       [models['model2'], models['model1'], models['model5']]),
            SymbolPath({symbols['A']},
                       [models['model1'], models['model2'], models['model4']]),
            SymbolPath({symbols['A']},
                       [models['model2'], models['model1'], models['model4']])
        ]

        self.assertTrue(
            len(paths_1) == len(ans_1), "Incorrect paths generated.")
        self.assertTrue(
            len(paths_2) == len(ans_2), "Incorrect paths generated.")
        for i in paths_1:
            self.assertTrue(i in ans_1, "Incorrect paths generated.")
        for i in paths_2:
            self.assertTrue(i in ans_2, "Incorrect paths generated.")
Пример #10
0
    def test_provenance(self):
        model4 = EquationModel(name="model4", equations=["D=B*C*11"], constraints=["G==0"])
        symbols = GraphTest.generate_canonical_symbols()
        models = GraphTest.generate_canonical_models()
        models['model4'] = model4
        del models['model6']
        material = GraphTest.generate_canonical_material(symbols)
        g = Graph(symbol_types=symbols, models=models, composite_models=dict())
        material_derived = g.evaluate(material)

        expected_quantities = [
            Quantity(symbols['A'], 19),
            Quantity(symbols['A'], 23),
            Quantity(symbols['B'], 38),
            Quantity(symbols['B'], 46),
            Quantity(symbols['C'], 57),
            Quantity(symbols['C'], 69),
            Quantity(symbols['G'], 95),
            Quantity(symbols['G'], 115),
            Quantity(symbols['F'], 266),
            Quantity(symbols['F'], 322),
            Quantity(symbols['D'], 70395),
            Quantity(symbols['D'], 85215),
            Quantity(symbols['D'], 103155)
        ]

        for q in material_derived._symbol_to_quantity[symbols['A']]:
            self.assertTrue(q._provenance is None)
        for q in material_derived._symbol_to_quantity[symbols['B']]:
            if q.value == 38:
                self.assertTrue(q._provenance.model is models['model1'].name,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[0] in q._provenance.inputs,
                                "provenance improperly calculated")
            else:
                self.assertTrue(q._provenance.model is models['model1'].name,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[1] in q._provenance.inputs,
                                "provenance improperly calculated")
        for q in material_derived._symbol_to_quantity[symbols['C']]:
            if q.value == 57:
                self.assertTrue(q._provenance.model is models['model1'].name,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[0] in q._provenance.inputs,
                                "provenance improperly calculated")
            else:
                self.assertTrue(q._provenance.model is models['model1'].name,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[1] in q._provenance.inputs,
                                "provenance improperly calculated")
        for q in material_derived._symbol_to_quantity[symbols['G']]:
            if q.value == 95:
                self.assertTrue(q._provenance.model is models['model2'].name,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[0] in q._provenance.inputs,
                                "provenance improperly calculated")
            else:
                self.assertTrue(q._provenance.model is models['model2'].name,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[1] in q._provenance.inputs,
                                "provenance improperly calculated")
        for q in material_derived._symbol_to_quantity[symbols['D']]:
            if q.value == 70395:
                self.assertTrue(q._provenance.model is models['model5'].name,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[4] in q._provenance.inputs,
                                "provenance improperly calculated")
                self.assertTrue(expected_quantities[6] in q._provenance.inputs,
                                "provenance improperly calculated")
Пример #11
0
    def test_symbol_ancestry_cyclic_constraint(self):
        """
        Tests the Symbol Ancestry algorithm on a cyclic graph with constraints.
        The canonical graph and the canonical material are used for this test.
        """
        model4 = EquationModel("model4", ['D=B*C*11'], constraints=["G==0"])
        symbols = GraphTest.generate_canonical_symbols()
        models = GraphTest.generate_canonical_models(constrain_model_4=True)
        models['model4'] = model4
        g = Graph(symbol_types=symbols, models=models, composite_models=dict())

        out1 = g.required_inputs_for_property(symbols['F'])

        self.assertTrue(out1.head.m is None and out1.head.parent is None and
                        out1.head.inputs == {symbols['F']} and len(out1.head.children) == 1,
                        "Tree head not properly defined.")
        self.assertTrue(out1.head.children[0].m == models['model3'] and
                        out1.head.children[0].inputs == {symbols['B']} and
                        out1.head.children[0].parent is out1.head and
                        len(out1.head.children[0].children) == 1,
                        "Tree branch improperly formed.")
        self.assertTrue(out1.head.children[0].children[0].m == models['model1'] and
                        out1.head.children[0].children[0].inputs == {symbols['A']} and
                        out1.head.children[0].children[0].parent is out1.head.children[0] and
                        len(out1.head.children[0].children) == 1 and
                        len(out1.head.children[0].children[0].children) == 0,
                        "Tree branch improperly formed.")

        out2 = g.required_inputs_for_property(symbols['D'])
        self.assertTrue(out2.head.m is None and out2.head.parent is None and
                        out2.head.inputs == {symbols['D']} and len(out2.head.children) == 2,
                        "Tree head not properly defined.")
        m_map = {x.m: x for x in out2.head.children}
        self.assertTrue(m_map[models['model4']].inputs == {symbols['B'], symbols['C'], symbols['G']} and
                        m_map[models['model4']].parent is out2.head,
                        "Tree branch improperly formed.")
        self.assertTrue(m_map[models['model5']].inputs == {symbols['C'], symbols['G']} and
                        m_map[models['model5']].parent is out2.head and
                        len(m_map[models['model5']].children) == 2,
                        "Tree branch improperly formed.")
        m_map_2 = {x.m: x for x in m_map[models['model5']].children}
        self.assertTrue(m_map_2[models['model1']].inputs == {symbols['G'], symbols['A']} and
                        m_map_2[models['model1']].parent is m_map[models['model5']] and
                        len(m_map_2[models['model1']].children) == 1 and
                        m_map_2[models['model1']].children[0].parent is m_map_2[models['model1']] and
                        m_map_2[models['model1']].children[0].children == [] and
                        m_map_2[models['model1']].children[0].inputs == {symbols['A']},
                        "Tree branch improperly formed.")
        self.assertTrue(m_map_2[models['model2']].inputs == {symbols['C'], symbols['A']} and
                        m_map_2[models['model2']].parent is m_map[models['model5']] and
                        len(m_map_2[models['model2']].children) == 1 and
                        m_map_2[models['model2']].children[0].parent is m_map_2[models['model2']] and
                        m_map_2[models['model2']].children[0].children == [] and
                        m_map_2[models['model2']].children[0].inputs == {symbols['A']},
                        "Tree branch improperly formed.")

        m_map_1 = {x.m: x for x in m_map[models['model4']].children}
        self.assertTrue(m_map_1[models['model1']].inputs == {symbols['G'], symbols['A']} and
                        m_map_1[models['model1']].parent is m_map[models['model4']] and
                        len(m_map_1[models['model1']].children) == 1 and
                        m_map_1[models['model1']].children[0].parent is m_map_1[models['model1']] and
                        m_map_1[models['model1']].children[0].children == [] and
                        m_map_1[models['model1']].children[0].inputs == {symbols['A']},
                        "Tree branch improperly formed.")
        self.assertTrue(m_map_1[models['model2']].inputs == {symbols['B'], symbols['C'], symbols['A']} and
                        m_map_1[models['model2']].parent is m_map[models['model4']] and
                        len(m_map_1[models['model2']].children) == 1 and
                        m_map_1[models['model2']].children[0].parent is m_map_1[models['model2']] and
                        m_map_1[models['model2']].children[0].children == [] and
                        m_map_1[models['model2']].children[0].inputs == {symbols['A']},
                        "Tree branch improperly formed.")
Пример #12
0
    def test_model_register_unregister(self):
        A = Symbol('a', ['A'], ['A'], units='dimensionless', shape=1)
        B = Symbol('b', ['B'], ['B'], units='dimensionless', shape=1)
        C = Symbol('c', ['C'], ['C'], units='dimensionless', shape=1)
        D = Symbol('d', ['D'], ['D'], units='dimensionless', shape=1)
        m = EquationModel('equation_model_to_remove', ['a = b * 3'],
                          variable_symbol_map={
                              'a': A,
                              'b': B
                          })
        self.assertIn(m.name, Registry("models"))
        self.assertTrue(m.registered)
        m.unregister()
        self.assertNotIn(m.name, Registry("models"))
        self.assertFalse(m.registered)
        m.register()
        self.assertTrue(m.registered)
        with self.assertRaises(KeyError):
            m.register(overwrite_registry=False)

        m.unregister()
        m = EquationModel('equation_model_to_remove', ['a = b * 3'],
                          variable_symbol_map={
                              'a': A,
                              'b': B
                          },
                          register=False)
        self.assertNotIn(m.name, Registry("models"))
        self.assertFalse(m.registered)

        m.register()
        with self.assertRaises(KeyError):
            _ = EquationModel('equation_model_to_remove', ['a = b * 3'],
                              variable_symbol_map={
                                  'a': A,
                                  'b': B
                              },
                              register=True,
                              overwrite_registry=False)

        m_replacement = EquationModel('equation_model_to_remove',
                                      ['c = d * 3'],
                                      variable_symbol_map={
                                          'c': C,
                                          'd': D
                                      })

        m_registered = Registry("models")['equation_model_to_remove']
        self.assertIs(m_registered, m_replacement)
        self.assertIsNot(m_registered, m)
Пример #13
0
from pkgutil import iter_modules
import os
from propnet.core.models import EquationModel, PyModuleModel,\
    PyModuleCompositeModel
from propnet.models import python, composite
from glob import glob

DEFAULT_MODELS = []
DEFAULT_COMPOSITE_MODELS = []

# Load equation models
EQUATION_MODEL_DIR = os.path.join(os.path.dirname(__file__), "serialized")
EQUATION_MODULE_FILES = glob(EQUATION_MODEL_DIR + '/*.yaml')
for filename in EQUATION_MODULE_FILES:
    model_path = os.path.join(EQUATION_MODEL_DIR, filename)
    model = EquationModel.from_file(model_path)
    DEFAULT_MODELS.append(model)

# Load python models
MODULE_LIST = iter_modules(python.__path__)
for _, module_name, _ in MODULE_LIST:
    module_path = "propnet.models.python.{}".format(module_name)
    DEFAULT_MODELS.append(PyModuleModel(module_path))

DEFAULT_MODEL_DICT = {d.name: d for d in DEFAULT_MODELS}
DEFAULT_MODEL_NAMES = list(DEFAULT_MODEL_DICT.keys())

# Load composite models
COMPOSITE_MODULE_LIST = iter_modules(composite.__path__)
for _, module_name, _ in COMPOSITE_MODULE_LIST:
    module_path = "propnet.models.composite.{}".format(module_name)