Example #1
0
def test_get_function():

    po = get_function("Powerlaw")

    _ = po(1.0)

    with pytest.raises(UnknownFunction):

        _ = get_function("not_existant")
Example #2
0
    def link(self, parameter_1, parameter_2, link_function=None):
        """
        Link the value of the provided parameters through the provided function (identity is the default, i.e.,
        parameter_1 = parameter_2).

        :param parameter_1: the first parameter
        :param parameter_2: the second parameter
        :param link_function: a function instance. If not provided, the identity function will be used by default.
        Otherwise, this link will be set: parameter_1 = link_function(parameter_2)
        :return: (none)
        """

        if link_function is None:
            # Use the Line function by default, with both parameters fixed so that the two
            # parameters to be linked will vary together
            link_function = get_function('Line')

            link_function.a.value = 1
            link_function.a.fix = True

            link_function.b.value = 0
            link_function.b.fix = True

        parameter_1.add_auxiliary_variable(parameter_2, link_function)

        # Now set the units of the link function
        link_function.set_units(parameter_2.unit, parameter_1.unit)
Example #3
0
    def link(self, parameter_1, parameter_2, link_function=None):
        """
        Link the value of the provided parameters through the provided function (identity is the default, i.e.,
        parameter_1 = parameter_2).

        :param parameter_1: the first parameter
        :param parameter_2: the second parameter
        :param link_function: a function instance. If not provided, the identity function will be used by default.
        Otherwise, this link will be set: parameter_1 = link_function(parameter_2)
        :return: (none)
        """

        assert parameter_1.path in self, "Parameter %s is not contained in this model" % parameter_1.path
        assert parameter_2.path in self, "Parameter %s is not contained in this model" % parameter_2.path

        if link_function is None:
            # Use the Line function by default, with both parameters fixed so that the two
            # parameters to be linked will vary together
            link_function = get_function('Line')

            link_function.a.value = 1
            link_function.a.fix = True

            link_function.b.value = 0
            link_function.b.fix = True

        parameter_1.add_auxiliary_variable(parameter_2, link_function)

        # Now set the units of the link function
        link_function.set_units(parameter_2.unit, parameter_1.unit)
Example #4
0
    def link(self, parameter_1, parameter_2, link_function=None) -> None:
        """
        Link the value of the provided parameters through the provided function (identity is the default, i.e.,
        parameter_1 = parameter_2).

        :param parameter_1: the first parameter;can be either a single parameter or a list of prarameters
        :param parameter_2: the second parameter
        :param link_function: a function instance. If not provided, the identity function will be used by default.
        Otherwise, this link will be set: parameter_1 = link_function(parameter_2)
        :return: (none)
        """
        if not isinstance(parameter_1, list):
            # Make a list of one element
            parameter_1_list = [parameter_1]
        else:
            # Make a copy to avoid tampering with the input
            parameter_1_list = list(parameter_1)

        for param_1 in parameter_1_list:
            if not param_1.path in self:

                log.error("Parameter %s is not contained in this model" %
                          param_1.path)

                raise AssertionError()

        if not parameter_2.path in self:

            log.error("Parameter %s is not contained in this model" %
                      parameter_2.path)

            raise AssertionError()

        if link_function is None:
            # Use the Line function by default, with both parameters fixed so that the two
            # parameters to be linked will vary together
            link_function = get_function("Line")

            link_function.a.value = 0
            link_function.a.fix = True

            link_function.b.value = 1
            link_function.b.fix = True

        for param_1 in parameter_1_list:
            param_1.add_auxiliary_variable(parameter_2, link_function)
            # Now set the units of the link function
            link_function.set_units(parameter_2.unit, param_1.unit)
Example #5
0
    def _parse_shape_definition(self,
                                component_name,
                                function_name,
                                parameters_definition,
                                is_spatial=False):

        # Get the function

        if "expression" in parameters_definition:

            # This is a composite function
            function_instance = function.get_function(
                function_name, parameters_definition["expression"])

        else:

            try:

                function_instance = function.get_function(function_name)

            except function.UnknownFunction:  # pragma: no cover

                raise ModelSyntaxError(
                    "Function %s, specified as shape for %s of source %s, is not a "
                    "known function" %
                    (function_name, component_name, self._source_name))

        # Loop over the parameters of the function instance, instead of the specification,
        # so we can understand if there are parameters missing from the specification

        for parameter_name, _ in function_instance.parameters.items():

            try:

                this_definition = parameters_definition[parameter_name]

            except KeyError:  # pragma: no cover

                raise ModelSyntaxError(
                    "Function %s, specified as shape for %s of source %s, lacks "
                    "the definition for parameter %s" %
                    (function_name, component_name, self._source_name,
                     parameter_name))

            # Update the parameter. Note that the order is important, because trying to set the value before the
            # minimum and maximum could result in a error.

            # All these specifications are optional. If they are not present, then the default value
            # already contained in the instance of the function will be used

            # Ignore for a second the RuntimeWarning that is printed if the default value in the function definition
            # is outside the bounds defined here

            with warnings.catch_warnings():

                warnings.simplefilter("ignore", RuntimeWarning)

                if "min_value" in this_definition:
                    function_instance.parameters[
                        parameter_name].min_value = this_definition[
                            "min_value"]

                if "max_value" in this_definition:
                    function_instance.parameters[
                        parameter_name].max_value = this_definition[
                            "max_value"]

            if "delta" in this_definition:
                function_instance.parameters[
                    parameter_name].delta = this_definition["delta"]

            if "free" in this_definition:
                function_instance.parameters[
                    parameter_name].free = this_definition["free"]

            if "unit" in this_definition:
                function_instance.parameters[parameter_name].unit = self._fix(
                    this_definition["unit"])

            # Now set the value, which must be present

            if "value" not in this_definition:  # pragma: no cover
                raise ModelSyntaxError(
                    "The parameter %s in function %s, specified as shape for %s "
                    "of source %s, lacks a 'value' attribute" %
                    (parameter_name, function_name, component_name,
                     self._source_name))

            # Check if this is a linked parameter, i.e., if 'value' is something like f(source.spectrum.powerlaw.index)

            matches = re.findall("""f\((.+)\)""",
                                 str(this_definition["value"]))

            if matches:

                # This is an expression which marks a parameter
                # with a link to another parameter (or an IndependentVariable such as time)

                # Get the variable
                linked_variable = matches[0]

                # Now get the law

                if "law" not in this_definition:  # pragma: no cover
                    raise ModelSyntaxError(
                        "The parameter %s in function %s, specified as shape for %s "
                        "of source %s, is linked to %s but lacks a 'law' attribute"
                        % (
                            parameter_name,
                            function_name,
                            component_name,
                            self._source_name,
                            linked_variable,
                        ))

                link_function_name = list(this_definition["law"].keys())[0]

                link_function_instance = self._parse_shape_definition(
                    component_name,
                    link_function_name,
                    this_definition["law"][link_function_name],
                )

                if is_spatial:
                    path = ".".join(
                        [self._source_name, function_name, parameter_name])
                else:
                    path = ".".join([
                        self._source_name,
                        "spectrum",
                        component_name,
                        function_name,
                        parameter_name,
                    ])

                self._links.append({
                    "parameter_path": path,
                    "law": link_function_instance,
                    "variable": linked_variable,
                })

            else:

                # This is a normal (not linked) parameter

                function_instance.parameters[
                    parameter_name].value = this_definition["value"]

            # Setup the prior for this parameter, if it exists
            if "prior" in this_definition:
                # Get the function for this prior

                # A name to display in case of errors

                name_for_errors = (
                    "prior for %s" %
                    function_instance.parameters[parameter_name].path)

                prior_function_name = list(this_definition["prior"].keys())[0]

                prior_function_definition = this_definition["prior"][
                    prior_function_name]

                prior_function = self._parse_shape_definition(
                    name_for_errors, prior_function_name,
                    prior_function_definition)

                # Set it as prior for current parameter

                function_instance.parameters[
                    parameter_name].prior = prior_function

        # Now handle extra_setup if any
        if "extra_setup" in parameters_definition:

            if is_spatial:
                path = ".".join([self._source_name, function_name])
            else:
                path = ".".join([
                    self._source_name, "spectrum", component_name,
                    function_name
                ])

            self._extra_setups.append({
                "function_path":
                path,
                "extra_setup":
                parameters_definition["extra_setup"],
            })

        return function_instance
Example #6
0
    def _parse_shape_definition(self, component_name, function_name,
                                parameters_definition):

        # Get the function

        if 'expression' in parameters_definition:

            # This is a composite function
            function_instance = function.get_function(
                function_name, parameters_definition['expression'])

        else:

            try:

                function_instance = function.get_function(function_name)

            except function.UnknownFunction:

                raise ModelSyntaxError(
                    "Function %s, specified as shape for %s of source %s, is not a "
                    "known function" %
                    (function_name, component_name, self._source_name))

        # Loop over the parameters of the function instance, instead of the specification,
        # so we can understand if there are parameters missing from the specification

        for parameter_name in function_instance.parameters.keys():

            try:

                this_definition = parameters_definition[parameter_name]

            except KeyError:

                raise ModelSyntaxError(
                    "Function %s, specified as shape for %s of source %s, lacks "
                    "the definition for parameter %s" %
                    (function_name, component_name, self._source_name,
                     parameter_name))

            # Update the parameter. Note that the order is important, because trying to set the value before the
            # minimum and maximum could result in a error.

            # All these specifications are optional. If they are not present, then the default value
            # already contained in the instance of the function will be used

            if 'min_value' in this_definition:
                function_instance.parameters[
                    parameter_name].min_value = this_definition['min_value']

            if 'max_value' in this_definition:
                function_instance.parameters[
                    parameter_name].max_value = this_definition['max_value']

            if 'delta' in this_definition:
                function_instance.parameters[
                    parameter_name].delta = this_definition['delta']

            if 'fix' in this_definition:
                function_instance.parameters[
                    parameter_name].fix = this_definition['fix']

            if 'free' in this_definition:
                function_instance.parameters[
                    parameter_name].free = this_definition['free']

            if 'unit' in this_definition:
                function_instance.parameters[parameter_name].unit = self._fix(
                    this_definition['unit'])

            # Now set the value, which must be present

            if 'value' not in this_definition:
                raise ModelSyntaxError(
                    "The parameter %s in function %s, specified as shape for %s "
                    "of source %s, lacks a 'value' attribute" %
                    (parameter_name, function_name, component_name,
                     self._source_name))

            # Check if this is a linked parameter, i.e., if 'value' is something like f(source.spectrum.powerlaw.index)

            matches = re.findall('''f\((.+)\)''',
                                 str(this_definition['value']))

            if matches:

                # This is an expression which marks a parameter
                # with a link to another parameter (or an IndependentVariable such as time)

                # Get the variable
                linked_variable = matches[0]

                # Now get the law

                if 'law' not in this_definition:
                    raise ModelSyntaxError(
                        "The parameter %s in function %s, specified as shape for %s "
                        "of source %s, is linked to %s but lacks a 'law' attribute"
                        % (parameter_name, function_name, component_name,
                           self._source_name, linked_variable))

                link_function_name = this_definition['law'].keys()[0]

                link_function_instance = self._parse_shape_definition(
                    component_name, link_function_name,
                    this_definition['law'][link_function_name])

                path = ".".join([
                    self._source_name, 'spectrum', component_name,
                    function_name, parameter_name
                ])

                self._links.append({
                    'parameter_path': path,
                    'law': link_function_instance,
                    'variable': linked_variable
                })

            else:

                # This is a normal (not linked) parameter

                function_instance.parameters[
                    parameter_name].value = this_definition['value']

            # Setup the prior for this parameter, if it exists
            if 'prior' in this_definition:
                # Get the function for this prior

                # A name to display in case of errors

                name_for_errors = 'prior for %s' % function_instance.parameters[
                    parameter_name].path

                prior_function_name = this_definition['prior'].keys()[0]

                prior_function_definition = this_definition['prior'][
                    prior_function_name]

                prior_function = self._parse_shape_definition(
                    name_for_errors, prior_function_name,
                    prior_function_definition)

                # Set it as prior for current parameter

                function_instance.parameters[
                    parameter_name].prior = prior_function

        # Now handle extra_setup if any
        if 'extra_setup' in parameters_definition:
            path = ".".join(
                [self._source_name, 'spectrum', component_name, function_name])

            self._extra_setups.append({
                'function_path':
                path,
                'extra_setup':
                parameters_definition['extra_setup']
            })

        return function_instance
Example #7
0
    def _parse_shape_definition(self, component_name, function_name, parameters_definition):

        # Get the function

        if 'expression' in parameters_definition:

            # This is a composite function
            function_instance = function.get_function(function_name, parameters_definition['expression'])

        else:

            try:

                function_instance = function.get_function(function_name)

            except function.UnknownFunction:

                raise ModelSyntaxError("Function %s, specified as shape for %s of source %s, is not a "
                                       "known function" % (function_name, component_name, self._source_name))

        # Loop over the parameters of the function instance, instead of the specification,
        # so we can understand if there are parameters missing from the specification

        for parameter_name in function_instance.parameters.keys():

            try:

                this_definition = parameters_definition[parameter_name]

            except KeyError:

                raise ModelSyntaxError("Function %s, specified as shape for %s of source %s, lacks "
                                       "the definition for parameter %s"
                                       % (function_name, component_name, self._source_name, parameter_name))

            # Update the parameter. Note that the order is important, because trying to set the value before the
            # minimum and maximum could result in a error.

            # All these specifications are optional. If they are not present, then the default value
            # already contained in the instance of the function will be used

            if 'min_value' in this_definition:
                function_instance.parameters[parameter_name].min_value = this_definition['min_value']

            if 'max_value' in this_definition:
                function_instance.parameters[parameter_name].max_value = this_definition['max_value']

            if 'delta' in this_definition:
                function_instance.parameters[parameter_name].delta = this_definition['delta']

            if 'fix' in this_definition:
                function_instance.parameters[parameter_name].fix = this_definition['fix']

            if 'free' in this_definition:
                function_instance.parameters[parameter_name].free = this_definition['free']

            if 'unit' in this_definition:
                function_instance.parameters[parameter_name].unit = self._fix(this_definition['unit'])

            # Now set the value, which must be present

            if 'value' not in this_definition:
                raise ModelSyntaxError("The parameter %s in function %s, specified as shape for %s "
                                       "of source %s, lacks a 'value' attribute"
                                       % (parameter_name, function_name, component_name, self._source_name))

            # Check if this is a linked parameter, i.e., if 'value' is something like f(source.spectrum.powerlaw.index)

            matches = re.findall('''f\((.+)\)''', str(this_definition['value']))

            if matches:

                # This is an expression which marks a parameter
                # with a link to another parameter (or an IndependentVariable such as time)

                # Get the variable
                linked_variable = matches[0]

                # Now get the law

                if 'law' not in this_definition:
                    raise ModelSyntaxError("The parameter %s in function %s, specified as shape for %s "
                                           "of source %s, is linked to %s but lacks a 'law' attribute"
                                           % (parameter_name, function_name, component_name,
                                              self._source_name, linked_variable))

                link_function_name = this_definition['law'].keys()[0]

                link_function_instance = self._parse_shape_definition(component_name, link_function_name,
                                                                      this_definition['law'][link_function_name])

                path = ".".join([self._source_name, 'spectrum', component_name, function_name, parameter_name])

                self._links.append({'parameter_path': path,
                                    'law': link_function_instance,
                                    'variable': linked_variable})

            else:

                # This is a normal (not linked) parameter

                function_instance.parameters[parameter_name].value = this_definition['value']

            # Setup the prior for this parameter, if it exists
            if 'prior' in this_definition:
                # Get the function for this prior

                # A name to display in case of errors

                name_for_errors = 'prior for %s' % function_instance.parameters[parameter_name].path

                prior_function_name = this_definition['prior'].keys()[0]

                prior_function_definition = this_definition['prior'][prior_function_name]

                prior_function = self._parse_shape_definition(name_for_errors,
                                                              prior_function_name,
                                                              prior_function_definition)

                # Set it as prior for current parameter

                function_instance.parameters[parameter_name].prior = prior_function

        # Now handle extra_setup if any
        if 'extra_setup' in parameters_definition:
            path = ".".join([self._source_name, 'spectrum', component_name, function_name])

            self._extra_setups.append({'function_path': path,
                                       'extra_setup': parameters_definition['extra_setup']})

        return function_instance