Example #1
0
    def get_aggregated_quantities(self):
        """
        Return mean values for all quantities for each symbol.

        Returns:
            (dict<Symbol, weighted_mean) mapping from a Symbol to
            an aggregated statistic.
        """
        # TODO: proper weighting system, and more flexibility in object handling
        aggregated = {}
        for symbol, quantities in self._quantities_by_symbol.items():
            if not symbol.category == 'object':
                aggregated[symbol] = NumQuantity.from_weighted_mean(
                    list(quantities))
        return aggregated
Example #2
0
 def rec_provenance_tree_check(self, q_storage, q_original, from_dict=False):
     self.assertIsInstance(q_storage, ProvenanceStore)
     self.assertEqual(q_storage.model, q_original.model)
     for v in q_storage.inputs or []:
         self.assertIsInstance(v, ProvenanceStoreQuantity)
         v_orig = [x for x in q_original.inputs
                   if x._internal_id == v._internal_id]
         self.assertEqual(len(v_orig), 1)
         v_orig = v_orig[0]
         if from_dict:
             self.assertFalse(v.has_value())
         elif NumQuantity.is_acceptable_type(v.value):
             self.assertTrue(np.isclose(v.value, v_orig.value))
             self.assertTrue(v.has_value())
         else:
             self.assertEqual(v.value, v_orig.value)
             self.assertTrue(v.has_value())
         self.assertListEqual(v.tags, v_orig.tags)
         self.rec_provenance_tree_check(v.provenance, v_orig.provenance, from_dict)
Example #3
0
    def setUp(self):
        # Inspiration was taken from the GraphTest class
        # I tried to construct the dictionaries for comparison
        # without writing out every one explicity by reusing
        # information where it was applicable.
        # If this is too unreadable, can change to writing it
        # out explicity in a JSON file and importing it. Would
        # still need to replace some fields dynamically.
        symbols = StorageTest.generate_symbols()

        self.custom_syms_as_dicts = {
            k: {'@module': 'propnet.core.symbols',
                '@class': 'Symbol',
                'name': k,
                'display_names': [k],
                'display_symbols': [k],
                'units': (1, ()),
                'shape': 1,
                'object_type': None,
                'comment': None,
                'category': 'property',
                'constraint': None,
                'default_value': None} for k in ['A', 'B', 'C']
        }
        self.custom_syms_as_dicts['C'].update(
            {"units": None,
             "shape": None,
             "object_type": "str",
             "category": "object"})

        self.custom_symbols_json = copy.deepcopy(self.custom_syms_as_dicts)
        for k in ['A', 'B']:
            self.custom_symbols_json[k]['units'] = [1, []]

        a = [QuantityFactory.create_quantity(symbols['A'], 19),
             QuantityFactory.create_quantity(symbols['A'], 23)]
        b = [QuantityFactory.create_quantity(symbols['B'], 38,
                                             provenance=ProvenanceElement(model='model1',
                                                                          inputs=[a[0]])),
             QuantityFactory.create_quantity(symbols['B'], 46,
                                             provenance=ProvenanceElement(model='model1',
                                                                          inputs=[a[1]]))]
        self.quantities_custom_symbol = {"A": a,
                                         "B": b}

        self.sq_custom_sym_as_dicts = {
            k: [{'@module': 'propnet.dbtools.storage',
                 '@class': 'StorageQuantity',
                 'internal_id': vv._internal_id,
                 'data_type': 'NumQuantity',
                 'symbol_type': symbols[k],
                 'value': vv.magnitude,
                 'units': 'dimensionless',
                 'provenance': ProvenanceStore.from_provenance_element(vv.provenance),
                 'tags': [],
                 'uncertainty': None} for vv in v] for k, v in self.quantities_custom_symbol.items()
        }

        provenances_json = {
            "A": [{'@module': 'propnet.dbtools.storage',
                   '@class': 'ProvenanceStore',
                   'model': None,
                   'inputs': None,
                   'source': aa.provenance.source} for aa in a]}
        provenances_json['B'] = [
            {'@module': 'propnet.dbtools.storage',
             '@class': 'ProvenanceStore',
             'model': 'model1',
             'inputs': [{'@module': 'propnet.dbtools.storage',
                         '@class': 'ProvenanceStoreQuantity',
                         'data_type': 'NumQuantity',
                         'symbol_type': self.custom_symbols_json['A'],
                         'internal_id': q.provenance.inputs[0]._internal_id,
                         'tags': [],
                         'provenance': p}],
             'source': q.provenance.source} for q, p in zip(b, provenances_json['A'])]

        self.sq_custom_sym_json = copy.deepcopy(self.sq_custom_sym_as_dicts)
        for sym in ['A', 'B']:
            for q, p in zip(self.sq_custom_sym_json[sym], provenances_json[sym]):
                q['symbol_type'] = self.custom_symbols_json[sym]
                q['provenance'] = p

        band_gaps = [QuantityFactory.create_quantity('band_gap', 3.3, 'eV'),
                     QuantityFactory.create_quantity('band_gap', 2.1, 'eV')]

        bg_ri_model = DEFAULT_MODEL_DICT['band_gap_refractive_index_moss']
        refractive_indices = [bg_ri_model.evaluate({"Eg": bg}).pop('refractive_index') for bg in band_gaps]

        self.quantities_canonical_symbol = {"band_gaps": band_gaps,
                                            "refractive_indices": refractive_indices}

        self.sq_canonical_sym_as_dicts_no_value = copy.deepcopy(self.sq_custom_sym_as_dicts)
        self.sq_canonical_sym_as_dicts_no_value['band_gaps'] = self.sq_canonical_sym_as_dicts_no_value.pop('A')
        self.sq_canonical_sym_as_dicts_no_value['refractive_indices'] = self.sq_canonical_sym_as_dicts_no_value.pop('B')

        for d, sq in zip(self.sq_canonical_sym_as_dicts_no_value['band_gaps'], band_gaps):
            d.update({
                "internal_id": sq._internal_id,
                "symbol_type": "band_gap",
                "units": "electron_volt",
                "provenance": ProvenanceStore.from_provenance_element(sq.provenance)
            })
            d.pop('value')

        for d, sq in zip(self.sq_canonical_sym_as_dicts_no_value['refractive_indices'], refractive_indices):
            d.update({
                "internal_id": sq._internal_id,
                "symbol_type": "refractive_index",
                "units": "dimensionless",
                "provenance": ProvenanceStore.from_provenance_element(sq.provenance)
            })
            d.pop('value')

        self.sq_canonical_sym_values = {"band_gaps": [3.3, 2.1],
                                        "refractive_indices": [2.316340583741216, 2.593439239956374]}

        provenances_json['band_gaps'] = [
            {'@module': 'propnet.dbtools.storage',
             '@class': 'ProvenanceStore',
             'model': None,
             'inputs': None,
             'source': bg.provenance.source}
            for bg in band_gaps
        ]

        provenances_json['refractive_indices'] = [{
            '@module': 'propnet.dbtools.storage',
            '@class': 'ProvenanceStore',
            'model': 'band_gap_refractive_index_moss',
            'inputs': [{'@module': 'propnet.dbtools.storage',
                        '@class': 'ProvenanceStoreQuantity',
                        'data_type': 'NumQuantity',
                        'symbol_type': 'band_gap',
                        'internal_id': bg._internal_id,
                        'tags': [],
                        'provenance': pj}],
            'source': ri.provenance.source}
            for bg, pj, ri in zip(band_gaps,
                                  provenances_json['band_gaps'],
                                  refractive_indices)
        ]

        self.sq_canonical_sym_json_no_value = copy.deepcopy(self.sq_canonical_sym_as_dicts_no_value)

        for sym in ["band_gaps", "refractive_indices"]:
            for q, p in zip(self.sq_canonical_sym_json_no_value[sym], provenances_json[sym]):
                q['provenance'] = p

        self.quantity_with_uncertainty = NumQuantity.from_weighted_mean(b)
        self.sq_with_uncertainty_as_dict_no_numbers = {
            '@module': 'propnet.dbtools.storage',
            '@class': 'StorageQuantity',
            'internal_id': self.quantity_with_uncertainty._internal_id,
            'data_type': 'NumQuantity',
            'symbol_type': symbols['B'],
            'units': 'dimensionless',
            'provenance': ProvenanceStore.from_provenance_element(
                self.quantity_with_uncertainty.provenance),
            'tags': []}

        provenances_json = {
            '@module': 'propnet.dbtools.storage',
            '@class': 'ProvenanceStore',
            'model': 'aggregation',
            'inputs': [
                {'@module': 'propnet.dbtools.storage',
                 '@class': 'ProvenanceStoreQuantity',
                 'data_type': 'NumQuantity',
                 'symbol_type': self.custom_symbols_json['B'],
                 'internal_id': b['internal_id'],
                 'tags': [],
                 'provenance': b['provenance']}
                for b in self.sq_custom_sym_json['B']],
            'source': self.quantity_with_uncertainty.provenance.source
        }

        self.sq_with_uncertainty_json_no_numbers = copy.deepcopy(self.sq_with_uncertainty_as_dict_no_numbers)
        self.sq_with_uncertainty_json_no_numbers.update({"symbol_type": self.custom_symbols_json['B'],
                                                         "provenance": provenances_json})
        self.sq_with_uncertainty_numbers = {"value": 42.0,
                                            "uncertainty": 4.0}

        obj_symbol = symbols['C']
        self.object_quantity = QuantityFactory.create_quantity(obj_symbol, "Test string")
        self.sq_object_as_dict = copy.deepcopy(self.sq_custom_sym_as_dicts['A'][0])
        self.sq_object_as_dict.update({
            "data_type": "ObjQuantity",
            "symbol_type": symbols['C'],
            "internal_id": self.object_quantity._internal_id,
            "value": "Test string",
            "units": None,
            "provenance": ProvenanceStore.from_provenance_element(self.object_quantity.provenance)
        })
        self.sq_object_json = copy.deepcopy(self.sq_object_as_dict)
        self.sq_object_json.update(
            {"symbol_type": self.custom_syms_as_dicts['C'],
             "provenance": {'@module': 'propnet.dbtools.storage',
                            '@class': 'ProvenanceStore',
                            'model': None,
                            'inputs': None,
                            'source': self.object_quantity.provenance.source}}
        )

        # This setting allows dict differences to be shown in full
        self.maxDiff = None
Example #4
0
    def evaluate(self, symbol_quantity_dict_in, allow_failure=True):
        """
        Given a set of property_values, performs error checking to see
        if the corresponding input symbol_values represents a valid
        input set based on the self.connections() method. If so, returns
        a dictionary representing the value of plug_in applied to the
        input_symbols. The dictionary contains a "successful" key
        representing if plug_in was successful.

        The key distinction between evaluate and plug_in is properties
        in properties out vs. symbols in symbols out.  In addition,
        evaluate also handles any requisite unit_mapping

        Args:
            symbol_quantity_dict ({property_name: Quantity}): a mapping of
                symbol names to quantities to be substituted
            allow_failure (bool): whether or not to catch
                errors in model evaluation

        Returns:
            dictionary of output properties with associated values
            generated from the input, along with "successful" if the
            substitution succeeds
        """
        # Remap symbols and units if symbol map isn't none

        symbol_quantity_dict = self.map_properties_to_symbols(
            symbol_quantity_dict_in)

        input_symbol_quantity_dict = {k: v for k, v in symbol_quantity_dict.items()
                                      if not (k in self.constraint_symbols
                                              and k not in self.all_input_symbols)}

        for (k, v) in symbol_quantity_dict.items():
            # replacing = self.symbol_property_map.get(k, k)
            replacing = self.symbol_property_map.get(k)
            # to_quantity() returns original object if it's already a BaseQuantity
            # unlike Quantity() which will return a deep copy
            symbol_quantity_dict[k] = QuantityFactory.to_quantity(replacing, v)

        # TODO: Is it really necessary to strip these?
        # TODO: maybe this only applies to pymodels or things with objects?
        # strip units from input and keep for reassignment
        symbol_value_dict = {}

        for symbol, quantity in symbol_quantity_dict.items():
            # If unit map convert and then scrub units
            if self.unit_map.get(symbol):
                quantity = quantity.to(self.unit_map[symbol])
                symbol_value_dict[symbol] = quantity.magnitude
            # Otherwise use values
            else:
                symbol_value_dict[symbol] = quantity.value

        contains_complex_input = any(NumQuantity.is_complex_type(v) for v in symbol_value_dict.values())
        input_symbol_value_dict = {k: symbol_value_dict[k] for k in input_symbol_quantity_dict.keys()}

        # Plug in and check constraints
        try:
            with PrintToLogger():
                out = self.plug_in(input_symbol_value_dict)
        except Exception as err:
            if allow_failure:
                return {"successful": False,
                        "message": "{} evaluation failed: {}".format(self, err)}
            else:
                raise err
        if not self.check_constraints({**symbol_value_dict, **out}):
            return {"successful": False,
                    "message": "Constraints not satisfied"}

        provenance = ProvenanceElement(
            model=self.name, inputs=list(input_symbol_quantity_dict.values()),
            source="propnet")

        out = self.map_symbols_to_properties(out)
        unit_map_as_properties = self.map_symbols_to_properties(self.unit_map)
        for symbol, value in out.items():
            try:
                quantity = QuantityFactory.create_quantity(
                    symbol, value, unit_map_as_properties.get(symbol),
                    provenance=provenance)
            except SymbolConstraintError as err:
                if allow_failure:
                    errmsg = "{} symbol constraint failed: {}".format(self, err)
                    return {"successful": False,
                            "message": errmsg}
                else:
                    raise err

            if quantity.contains_nan_value():
                return {"successful": False,
                        "message": "Evaluation returned invalid values (NaN)"}
            # TODO: Update when we figure out how we're going to handle complex quantities
            # Model evaluation will fail if complex values are returned when no complex input was given
            # Can surely handle this more gracefully, or assume that the users will apply constraints
            if quantity.contains_imaginary_value() and not contains_complex_input:
                return {"successful": False,
                        "message": "Evaluation returned invalid values (complex)"}

            out[symbol] = quantity

        out['successful'] = True
        return out