Distributions are mostly useful when using samplers (which we'll see in the next tutorial on solving the inverse problem) - but can also be useful to propagate any set of distributions (whether those be uncertainties in the literature, etc) through the forward model.
Let's first make sure we have the latest version of PHOEBE 2.3 installed (uncomment this line if running in an online notebook session such as colab).
In [ ]:
#!pip install -I "phoebe>=2.3,<2.4"
In [1]:
%matplotlib inline
In [4]:
import phoebe
logger = phoebe.logger()
b = phoebe.default_binary()
b.add_dataset('lc', compute_phases=phoebe.linspace(0,1,101))
Out[4]:
Distributions can be attached to most any FloatParameter in the Bundle. To see a list of these available parameters, we can call b.get_adjustable_parameters. Note the exclude_constrained
option which defaults to True: we can set distributions on constrained parameters (for priors, for example), but those will not be able to be sampled from in the forward model or while fitting. We'll come back to this in the next tutorial when looking at priors.
In [5]:
b.get_adjustable_parameters()
Out[5]:
add_distribution is quite flexible and accepts several different syntaxes to add multiple distributions in one line. Here we'll just attach a distribution to a single parameter at a time. Just like when calling add_dataset or add_compute, add_distribution optionally takes a distribution
tag -- and in the cases of distributions, we can attach distributions to multiple parameters with the same distribution
tag.
The values of the DistributionParameters are distl distribution objects -- the most common of which are conveniently available at the top-level of PHOEBE:
Now let's attach a gaussian distribution on the temperature of the primary star.
In [6]:
b.add_distribution(qualifier='teff', component='primary',
value=phoebe.gaussian(6000,100),
distribution='mydist')
Out[6]:
As you probably can expect by now, we also have methods to:
In [7]:
print(b.get_distribution(distribution='mydist'))
Now let's add another distribution, with the same distribution
tag, to the inclination of the binary.
In [8]:
b.add_distribution(qualifier='incl', component='binary',
value=phoebe.uniform(80,90),
distribution='mydist')
Out[8]:
In [9]:
print(b.get_distribution(distribution='mydist'))
The parameters we've created and attached are DistributionParameters and live in context='distribution'
, with all other tags matching the parameter they're referencing. For example, let's filter and look at the distributions we've added.
In [10]:
print(b.filter(context='distribution'))
In [11]:
print(b.get_parameter(context='distribution', qualifier='incl'))
In [12]:
print(b.get_parameter(context='distribution', qualifier='incl').tags)
The "value" of the parameter, is the distl distributon object itself.
In [14]:
b.get_value(context='distribution', qualifier='incl')
Out[14]:
And because of that, we can call any method on the distl object, including plotting the distribution.
In [17]:
_ = b.get_value(context='distribution', qualifier='incl').plot(show=True)
If we want to see how multiple individual distributions interact and are correlated with each other via a corner plot, we can access the combined "distribution collection" from any number of distribution
tags. This may not be terribly useful now, but is very useful when trying to create multivariate priors.
In [18]:
_ = b.plot_distribution_collection(distribution='mydist', show=True)
We can also sample from these distributions - either manually by calling sample on the distl or in bulk by respecting any covariances in the "distributon collection" via:
In [19]:
b.sample_distribution_collection(distribution='mydist')
Out[19]:
By default this just returns a dictionary with the twigs and sampled values. But if we wanted, we could have these applied immediately to the face-values by passing set_value=True
, in which case a ParameterSet of changed parameters (including those via constraints) is returned instead.
In [21]:
changed_params = b.sample_distribution_collection(distribution='mydist', set_value=True)
In [22]:
print(changed_params)
Lastly, we can have PHOEBE automatically draw from a "distribution collection" multiple times and expose the distribution of the model itself.
In [24]:
print(b.get_parameter(qualifier='sample_from', context='compute'))
Once sample_from
is set, sample_num
and sample_mode
are exposed as visible parameters
In [25]:
b.set_value('sample_from', value='mydist')
In [26]:
print(b.filter(qualifier='sample*'))
Now when we call run_compute, 10 different instances of the forward model will be computed from 10 random draws from the "distribution collection" but only the median and 1-sigma uncertainties will be exposed in the model.
In [27]:
b.run_compute(irrad_method='none')
Out[27]:
In [28]:
_ = b.plot(show=True)
Next up: let's learn about solving the inverse problem