-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathlog_gaussian.py
More file actions
142 lines (112 loc) · 3.99 KB
/
log_gaussian.py
File metadata and controls
142 lines (112 loc) · 3.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
from typing import Optional
import numpy as np
from autofit.messages.normal import NormalMessage
from .abstract import Prior
from ...messages.composed_transform import TransformedMessage
from ...messages.transform import log_transform
class LogGaussianPrior(Prior):
__identifier_fields__ = ("mean", "sigma")
__database_args__ = ("mean", "sigma", "id_")
def __init__(
self,
mean: float,
sigma: float,
id_: Optional[int] = None,
):
r"""
A prior for a variable whose logarithm is gaussian distributed. Work in natural log.
The conversion of an input unit value, ``u``, to a physical value, ``p``, via the prior is as follows:
.. math::
p = \mu + (\sigma * sqrt(2) * erfcinv(2.0 * (1.0 - u))
For example for ``prior = LogGaussianPrior(mean=1.0, sigma=2.0)``, an
input ``prior.value_for(unit=0.5)`` is equal to 1.0.
[Rich describe how this is done via message]
Parameters
----------
mean
The *natural log* of the distribution's mean.
sigma
The spread of this distribution in *natural log* space, e.g. sigma=1.0 means P(ln x) has a
standard deviation of 1.
Examples
--------
prior = af.LogGaussianPrior(mean=1.0, sigma=2.0)
physical_value = prior.value_for(unit=0.5)
"""
self.mean = mean
self.sigma = sigma
message = TransformedMessage(
NormalMessage(mean, sigma),
log_transform,
)
super().__init__(
message=message,
id_=id_,
)
def tree_flatten(self):
return (
self.mean,
self.sigma,
self.id,
), ()
@classmethod
def with_limits(cls, lower_limit: float, upper_limit: float) -> "LogGaussianPrior":
"""
Create a new gaussian prior centred between two limits
with sigma distance between this limits.
Note that these limits are not strict so exceptions will not
be raised for values outside of the limits.
This function is typically used in prior passing, where the
result of a model-fit are used to create new Gaussian priors
centred on the previously estimated median PDF model.
Parameters
----------
lower_limit
The lower limit of the new Gaussian prior.
upper_limit
The upper limit of the new Gaussian Prior.
Returns
-------
A new GaussianPrior
"""
return cls(
mean=(lower_limit + upper_limit) / 2,
sigma=upper_limit - lower_limit,
lower_limit=lower_limit,
upper_limit=upper_limit,
)
def _new_for_base_message(self, message):
"""
Create a new instance of this wrapper but change the parameters used
to instantiate the underlying message. This is useful for retaining
the same transform stack after recreating the underlying message during
projection.
"""
return LogGaussianPrior(
*message.parameters,
lower_limit=self.lower_limit,
upper_limit=self.upper_limit,
id_=self.instance().id,
)
def value_for(self, unit: float) -> float:
"""
Return a physical value for a value between 0 and 1 with the transformation
described by this prior.
Parameters
----------
unit
A unit value between 0 and 1.
Returns
-------
A physical value, mapped from the unit value accoridng to the prior.
"""
return super().value_for(unit)
@property
def parameter_string(self) -> str:
return f"mean = {self.mean}, sigma = {self.sigma}"
def log_prior_from_value(self, value, xp=np):
if value <= 0:
return float("-inf")
return self.message.base_message.log_prior_from_value(np.log(value)) - np.log(
value
)