Note:
This page was generated from a Jupyter notebook which can be found at
docs/tutorial_notebooks/5_parameter_space.ipynb
in your DYNAMITE directory.
—-
5. Parameter Space¶
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.
We’ll start as before by reading the same configuration file as previously,
[1]:
import dynamite as dyn
# read the config file
fname = 'NGC6278_config.yaml'
c = dyn.config_reader.Configuration(fname, reset_logging=True)
[INFO] 21:49:20 - dynamite.config_reader.Configuration - Config file NGC6278_config.yaml read.
[INFO] 21:49:20 - dynamite.config_reader.Configuration - io_settings...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - Output directory tree: NGC6278_output/.
[INFO] 21:49:20 - dynamite.config_reader.Configuration - system_attributes...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - model_components...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - system_parameters...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - orblib_settings...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - weight_solver_settings...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - Will attempt to recover partially run models.
[INFO] 21:49:20 - dynamite.config_reader.Configuration - parameter_space_settings...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - multiprocessing_settings...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - ... using 4 CPUs for orbit integration.
[INFO] 21:49:20 - dynamite.config_reader.Configuration - ... using 4 CPUs for weight solving.
[INFO] 21:49:20 - dynamite.config_reader.Configuration - legacy_settings...
[INFO] 21:49:20 - dynamite.config_reader.Configuration - System assembled
[INFO] 21:49:20 - dynamite.config_reader.Configuration - Configuration validated
[INFO] 21:49:20 - dynamite.config_reader.Configuration - Instantiated parameter space
[INFO] 21:49:20 - dynamite.model.AllModels - Previous models have been found: Reading NGC6278_output/all_models.ecsv into AllModels.table
[INFO] 21:49:20 - dynamite.config_reader.Configuration - Instantiated AllModels object
[INFO] 21:49:20 - dynamite.model.AllModels - No all_models table update required.
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
[2]:
# extract the parameter space
parspace = c.parspace
print('type of parspace is', type(parspace))
print('length of parspace is', len(parspace))
print('the parameter names are:')
for par in parspace:
print(' -', par.name)
type of parspace is <class 'dynamite.parameter_space.ParameterSpace'>
length of parspace is 8
the parameter names are:
- m-bh
- a-bh
- c-dh
- f-dh
- q-stars
- p-stars
- u-stars
- ml
Several properties are specified for each parameter in the configuration file. Let’s look at the value,
[3]:
print('Parameter / value in config file:')
for par in c.parspace:
print(f' {par.name} = {par.raw_value}')
Parameter / value in config file:
m-bh = 5.0
a-bh = 0.001
c-dh = 8.0
f-dh = 1.0
q-stars = 0.54
p-stars = 0.99
u-stars = 0.9999
ml = 5.0
These are the starting values from which we would like to run a model.
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
parameters -> XXX -> logarithmic : True
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.
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
Parameter.par_value
property. So to convert the above list from raw values, we can do the following,
[4]:
print('Parameter / value in linear units:')
for par in c.parspace:
print(f' {par.name} = {par.par_value}')
Parameter / value in linear units:
m-bh = 100000.0
a-bh = 0.001
c-dh = 8.0
f-dh = 10.0
q-stars = 0.54
p-stars = 0.99
u-stars = 0.9999
ml = 5.0
Notice how only those parameters which have been specified with logarithmic : True
have been modified.
Another property that we specifie for each parameter is whether or not it is fixed, a boolean value,
[5]:
for par in parspace:
if par.fixed:
fix_string = ' is fixed.'
if not par.fixed:
fix_string = ' is NOT fixed.'
print(f'{par.name}{fix_string}')
m-bh is fixed.
a-bh is fixed.
c-dh is fixed.
f-dh is NOT fixed.
q-stars is fixed.
p-stars is fixed.
u-stars is fixed.
ml is NOT fixed.
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,
[6]:
for par in parspace:
if not par.fixed:
tmp = par.par_generator_settings
lo, hi, step = tmp['lo'], tmp['hi'], tmp['step']
print(f'{par.name} takes step-size {step} and bounds ({lo,hi})')
f-dh takes step-size 0.5 and bounds ((-1.5, 1.5))
ml takes step-size 4.0 and bounds ((1.0, 9.0))
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.
In general, an algorithm to search through parameter space will take as input 1. the output of all models which have been run so far (e.g. \(\chi^2\) values) 2. setting for the free parameters (e.g. step-size and bounds) The algorithm will then output a new list of parameters for which we want to run models.
In DYNAMITE, we implement this generic idea in the class dyn.parameter_space.ParameterGenerator
. In the configuration file, you specify which parameter generator you would like to use, at the location
parameter_space_settings -> generator_type
The current choice is
[7]:
c.settings.parameter_space_settings['generator_type']
[7]:
'LegacyGridSearch'
This parameter generator requires an additional setting which is set at
parameter_space_settings -> generator_settings -> threshold_del_chi2_abs
or
parameter_space_settings -> generator_settings -> threshold_del_chi2_as_frac_of_sqrt2nobs
(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,
[8]:
threshold_del_chi2_as_frac_of_sqrt2nobs = \
c.settings.parameter_space_settings['generator_settings']['threshold_del_chi2_as_frac_of_sqrt2nobs']
threshold_del_chi2 = c.settings.parameter_space_settings['generator_settings']['threshold_del_chi2']
print(f'threshold_del_chi2_as_frac_of_sqrt2nobs = {threshold_del_chi2_as_frac_of_sqrt2nobs}')
print(f'threshold_del_chi2 = {threshold_del_chi2}')
threshold_del_chi2_as_frac_of_sqrt2nobs = 0.1
threshold_del_chi2 = 3.4871191548325395
The algorithm implemented to generate parameters in LegacyGridSearch
is the following,
iteration = 0
if iteration == 0
all parameters take `value` specified in the config
else:
1. find the model with the lowest chi-squared
2. find all models with chi-squared within threshold_del_chi2 of the lowest value
3. for all models satisfying that criteria:
- for all free parameters:
- generate a new parameter set +/-1 step-size from the current value
4. Remove any models with parameters outside specified bounds
5. iteration = iteration + 1
stop if no new models are added, or any other stopping criteria are met
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.
The last line of the algorithm mentions stopping criteria. Settings which control the stopping criteria are also speicified in the configuration file, under
parameter_space_settings -> stopping_criteria
The current settings which are the following,
[9]:
stopping_crierita = c.settings.parameter_space_settings['stopping_criteria']
for key in stopping_crierita:
print(f'{key} = {stopping_crierita[key]}')
min_delta_chi2_abs = 0.5
n_max_mods = 15
n_max_iter = 4
These have the following meaning,
if no new model impoves the chi-squared by at least
min_delta_chi2
, then stopif we have already run
n_max_mods
models, then stopif we have already run
n_max_iter
iterations, then stop
:)
[ ]: