-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathanalysis.py
133 lines (112 loc) · 4 KB
/
analysis.py
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
from typing import Optional
import numpy as np
from autofit.graphical.expectation_propagation import AbstractFactorOptimiser
from autofit.mapper.model import ModelInstance
from autofit.mapper.prior_model.prior_model import AbstractPriorModel
from autofit.non_linear.analysis import Analysis
from autofit.non_linear.paths.abstract import AbstractPaths
from .abstract import AbstractModelFactor
class FactorCallable:
def __init__(
self,
prior_model: AbstractPriorModel,
analysis: Analysis,
):
self.prior_model = prior_model
self.analysis = analysis
def __call__(self, **kwargs: np.ndarray) -> float:
"""
Returns an instance of the prior model and evaluates it, forming
a factor.
Parameters
----------
kwargs
Arguments with names that are unique for each prior.
Returns
-------
Calculated likelihood
"""
arguments = dict()
for name_, array in kwargs.items():
prior_id = int(name_.split("_")[1])
prior = self.prior_model.prior_with_id(prior_id)
arguments[prior] = array
# noinspection PyTypeChecker
instance = self.prior_model.instance_for_arguments(arguments)
return self.analysis.log_likelihood_function(instance)
class AnalysisFactor(AbstractModelFactor):
@property
def prior_model(self):
return self._prior_model
def __init__(
self,
prior_model: AbstractPriorModel,
analysis: Analysis,
optimiser: Optional[AbstractFactorOptimiser] = None,
name=None,
):
"""
A factor in the graph that actually computes the likelihood of a model
given values for each variable that model contains
Parameters
----------
prior_model
A model with some dimensionality
analysis
A class that implements a function which evaluates how well an
instance of the model fits some data
optimiser
A custom optimiser that will be used to fit this factor specifically
instead of the default optimiser
"""
self.label = prior_model.label
self.analysis = analysis
prior_variable_dict = {prior.name: prior for prior in prior_model.priors}
super().__init__(
prior_model=prior_model,
factor=FactorCallable(
prior_model=prior_model,
analysis=analysis,
),
optimiser=optimiser,
prior_variable_dict=prior_variable_dict,
name=name,
)
def __getstate__(self):
return self.__dict__
def __setstate__(self, state):
self.__dict__.update(state)
def __getattr__(self, item):
try:
return super().__getattr__(item)
except AttributeError:
return getattr(self.prior_model, item)
def name_for_variable(self, variable):
path = ".".join(self.prior_model.path_for_prior(variable))
return f"{self.name}.{path}"
def visualize(
self, paths: AbstractPaths, instance: ModelInstance, during_analysis: bool
):
"""
Visualise the instances provided using each factor.
Instances in the ModelInstance must have the same order as the factors.
Parameters
----------
paths
Object describing where data should be saved to
instance
A collection of instances, each corresponding to a factor
during_analysis
Is this visualisation during analysis?
"""
self.analysis.visualize(
paths=paths, instance=instance, during_analysis=during_analysis
)
self.analysis.visualize_combined(
analyses=None,
paths=paths,
instance=instance,
during_analysis=during_analysis,
)
def log_likelihood_function(self, instance: ModelInstance) -> float:
return self.analysis.log_likelihood_function(instance)