Ejemplo n.º 1
0
    def test_complex_and_imaginary_checking(self):
        A = Symbol('a', ['A'], ['A'], units='dimensionless', shape=1)
        B = Symbol('b', ['B'], ['B'], units='dimensionless', shape=[2, 2])
        # TODO: Revisit this when splitting quantity class into non-numerical and numerical
        C = Symbol('c', ['C'], ['C'], category='object', shape=1)

        real_float_scalar = Quantity(A, 1.0)
        real_float_non_scalar = Quantity(B, [[1.0, 1.0], [1.0, 1.0]])

        complex_scalar = Quantity(A, complex(1 + 1j))
        complex_non_scalar = Quantity(
            B, [[complex(1.0), complex(1.j)], [complex(1.j),
                                               complex(1.0)]])

        complex_scalar_zero_imaginary = Quantity(A, complex(1.0))
        complex_non_scalar_zero_imaginary = Quantity(
            B, [[complex(1.0), complex(1.0)], [complex(1.0),
                                               complex(1.0)]])

        complex_scalar_appx_zero_imaginary = Quantity(A, complex(1.0 + 1e-10j))
        complex_non_scalar_appx_zero_imaginary = Quantity(
            B, [[complex(1.0), complex(1.0 + 1e-10j)],
                [complex(1.0 + 1e-10j), complex(1.0)]])

        non_numerical = Quantity(C, 'test')

        # Test is_complex_type() with...
        # ...Quantity objects
        self.assertFalse(Quantity.is_complex_type(real_float_scalar))
        self.assertFalse(Quantity.is_complex_type(real_float_non_scalar))
        self.assertTrue(Quantity.is_complex_type(complex_scalar))
        self.assertTrue(Quantity.is_complex_type(complex_non_scalar))
        self.assertTrue(
            Quantity.is_complex_type(complex_scalar_zero_imaginary))
        self.assertTrue(
            Quantity.is_complex_type(complex_non_scalar_zero_imaginary))
        self.assertTrue(
            Quantity.is_complex_type(complex_scalar_appx_zero_imaginary))
        self.assertTrue(
            Quantity.is_complex_type(complex_non_scalar_appx_zero_imaginary))
        self.assertFalse(Quantity.is_complex_type(non_numerical))

        # ...primitive types
        self.assertFalse(Quantity.is_complex_type(1))
        self.assertFalse(Quantity.is_complex_type(1.))
        self.assertTrue(Quantity.is_complex_type(1j))
        self.assertFalse(Quantity.is_complex_type('test'))

        # ...np.array types
        self.assertFalse(Quantity.is_complex_type(np.array([1])))
        self.assertFalse(Quantity.is_complex_type(np.array([1.])))
        self.assertTrue(Quantity.is_complex_type(np.array([1j])))
        self.assertFalse(Quantity.is_complex_type(np.array(['test'])))

        # ...ureg Quantity objects
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity(1)))
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity(1.)))
        self.assertTrue(Quantity.is_complex_type(ureg.Quantity(1j)))
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity([1])))
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity([1.])))
        self.assertTrue(Quantity.is_complex_type(ureg.Quantity([1j])))

        # Check member functions
        self.assertFalse(real_float_scalar.contains_complex_type())
        self.assertFalse(real_float_scalar.contains_imaginary_value())
        self.assertFalse(real_float_non_scalar.contains_complex_type())
        self.assertFalse(real_float_non_scalar.contains_imaginary_value())

        self.assertTrue(complex_scalar.contains_complex_type())
        self.assertTrue(complex_scalar.contains_imaginary_value())
        self.assertTrue(complex_non_scalar.contains_complex_type())
        self.assertTrue(complex_non_scalar.contains_imaginary_value())

        self.assertTrue(complex_scalar_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_scalar_zero_imaginary.contains_imaginary_value())
        self.assertTrue(
            complex_non_scalar_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_non_scalar_zero_imaginary.contains_imaginary_value())

        self.assertTrue(
            complex_scalar_appx_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_scalar_appx_zero_imaginary.contains_imaginary_value())
        self.assertTrue(
            complex_non_scalar_appx_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_non_scalar_appx_zero_imaginary.contains_imaginary_value())

        self.assertFalse(non_numerical.contains_complex_type())
        self.assertFalse(non_numerical.contains_imaginary_value())
Ejemplo n.º 2
0
    def evaluate(self, symbol_quantity_dict, 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)

        for (k, v) in symbol_quantity_dict.items():
            replacing = self.symbol_property_map.get(k, k)
            symbol_quantity_dict[k] = Quantity.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(Quantity.is_complex_type(v) for v in symbol_value_dict.values())
        # Plug in and check constraints
        try:
            with PrintToLogger():
                out = self.plug_in(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(symbol_quantity_dict.values()))
        out = self.map_symbols_to_properties(out)
        for symbol, value in out.items():
            try:
                quantity = Quantity(symbol, value, self.unit_map.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