def test_get_function(): po = get_function("Powerlaw") _ = po(1.0) with pytest.raises(UnknownFunction): _ = get_function("not_existant")
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)
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)
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)
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
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
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