{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 5. Parameter Space" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To run a DYNAMITE model, one must specify a number of parameters for the gravitational potential. The aim of this notebook is to demonstrate how to specify these parameters and to highlight features that we have implemented in order to help you explore parameter space. \n", "\n", "We'll start as before by reading the same configuration file as previously," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import dynamite as dyn\n", "\n", "# read the config file\n", "fname = 'NGC6278_config.yaml'\n", "c = dyn.config_reader.Configuration(fname, reset_logging=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "When the configuration object is created, internally, a parameter space object is created. This ``parspace`` object is a list, and every entry of this list is a parameter in our model, Lets extract this and have a look" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# extract the parameter space \n", "parspace = c.parspace\n", "print('type of parspace is', type(parspace))\n", "print('length of parspace is', len(parspace))\n", "print('the parameter names are:')\n", "for par in parspace:\n", " print(' -', par.name)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Several properties are specified for each parameter in the configuration file. Let's look at the value," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('Parameter / value in config file:')\n", "for par in c.parspace:\n", " print(f' {par.name} = {par.raw_value}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These are the starting values from which we would like to run a model.\n", "\n", "One complication in specifying these values is that, for some parameters, we would like to take logarithmically spaced steps through parameter space, i.e. the ones which are specificed as\n", "```\n", "parameters -> XXX -> logarithmic : True\n", "```\n", "Logarithmic spacing can be useful for mass parameters. For other parameters (e.g. length scales) linearly spaced steps may be more appropriate. For other types of parameters (e.g. angles) a different spacing altogether may be preferable.\n", "\n", "To handle these possibilities, we introduce the concept of ``raw`` parameter values, distinct from the values themselves. All values associated with parameters in the configuration file are given in ``raw`` units. When we step through parameter space, we take linear steps in ``raw`` values. The conversion from raw values to the parameter values is handled by the Parameter class and the parameter values are accessible via the\n", "```\n", "Parameter.par_value\n", "```\n", "property. So to convert the above list from raw values, we can do the following," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "print('Parameter / value in linear units:')\n", "for par in c.parspace:\n", " print(f' {par.name} = {par.par_value}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice how only those parameters which have been specified with ``logarithmic : True`` have been modified.\n", "\n", "Another property that we specifie for each parameter is whether or not it is fixed, a boolean value," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for par in parspace:\n", " if par.fixed:\n", " fix_string = ' is fixed.'\n", " if not par.fixed:\n", " fix_string = ' is NOT fixed.'\n", " print(f'{par.name}{fix_string}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The only parameters which are not fixed for this example are the dark matter fraction ``f-dh`` and the mass-to-light ratio ``ml``. For these free parameters, additional properties about how search through parameter space are stored in the ``par_generator_settings`` attribute," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "for par in parspace:\n", " if not par.fixed:\n", " tmp = par.par_generator_settings\n", " lo, hi, step = tmp['lo'], tmp['hi'], tmp['step']\n", " print(f'{par.name} takes step-size {step} and bounds ({lo,hi})')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "How do we search over these free parameters? Running models (especially calcuating the orbit library) is expensive, so we will want to search through parameter space in the most efficient way possible.\n", "\n", "In general, an algorithm to search through parameter space will take as input\n", "1. the output of all models which have been run so far (e.g. $\\chi^2$ values)\n", "2. setting for the free parameters (e.g. step-size and bounds)\n", "The algorithm will then output a new list of parameters for which we want to run models.\n", "\n", "In DYNAMITE, we implement this generic idea in the class\n", "``dyn.parameter_space.ParameterGenerator``.\n", "In the configuration file, you specify *which* parameter generator you would like to use, at the location\n", "```\n", "parameter_space_settings -> generator_type\n", "```\n", "The current choice is " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "c.settings.parameter_space_settings['generator_type']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This parameter generator requires an additional setting which is set at\n", "```\n", "parameter_space_settings -> generator_settings -> threshold_del_chi2_abs\n", "```\n", "or\n", "```\n", "parameter_space_settings -> generator_settings -> threshold_del_chi2_as_frac_of_sqrt2nobs\n", "```\n", "(the options are mutually exclusive, set one or the other). Internally, the setting is converted to the appropriate ``threshold_del_chi2`` and is accessed in the following way," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "threshold_del_chi2_as_frac_of_sqrt2nobs = \\\n", " c.settings.parameter_space_settings['generator_settings']['threshold_del_chi2_as_frac_of_sqrt2nobs']\n", "threshold_del_chi2 = c.settings.parameter_space_settings['generator_settings']['threshold_del_chi2']\n", "print(f'threshold_del_chi2_as_frac_of_sqrt2nobs = {threshold_del_chi2_as_frac_of_sqrt2nobs}')\n", "print(f'threshold_del_chi2 = {threshold_del_chi2}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The algorithm implemented to generate parameters in ``LegacyGridSearch`` is the following,\n", "\n", "```\n", "iteration = 0\n", "if iteration == 0\n", " all parameters take `value` specified in the config\n", "else:\n", " 1. find the model with the lowest chi-squared\n", " 2. find all models with chi-squared within threshold_del_chi2 of the lowest value\n", " 3. for all models satisfying that criteria:\n", " - for all free parameters:\n", " - generate a new parameter set +/-1 step-size from the current value\n", " 4. Remove any models with parameters outside specified bounds\n", " 5. iteration = iteration + 1\n", "stop if no new models are added, or any other stopping criteria are met \n", "```\n", "\n", "For those of you who have used the previous version of the trixial Schwarzschild modelling code (aka ``schwpy``), this is the same algorithm which was implemented there.\n", "\n", "The last line of the algorithm mentions stopping criteria. Settings which control the stopping criteria are also speicified in the configuration file, under\n", "```\n", "parameter_space_settings -> stopping_criteria\n", "```\n", "The current settings which are the following," ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "stopping_crierita = c.settings.parameter_space_settings['stopping_criteria']\n", "for key in stopping_crierita:\n", " print(f'{key} = {stopping_crierita[key]}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "These have the following meaning,\n", "\n", "- if no new model impoves the chi-squared by at least ``min_delta_chi2``, then stop\n", "- if we have already run ``n_max_mods`` models, then stop\n", "- if we have already run ``n_max_iter`` iterations, then stop\n", "\n", ":)\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }