Define how sources should be defined
There are several questions to be answered with respect to how a source is defined. Let's resolve this here.
I can see three ways the source can be implemented. I'm going to list these first, along with how they would then be used by the sampler (in any of the three cases, the user will probably just input a string saying they want a 'cbc' waveform etc).
A) Class with parameters defined at initialisation
Definition:
class Source:
def __init__(self, name, parameters):
self.name = name
self.parameters = parameters
def model(self, time):
return parameters['m'] * time + parameters['c']
def update_parameters(self, parameters):
self.parameters = parameters
Use in the sampler:
# Somewhere in the initialisation of the sampler
my_source = Source('my_source', parameters)
...
# Somewhere in the likelihood
my_source.update_parameters(parameters)
waveform = my_source.model(time)
B) Class with parameters passed to model
Definition:
class Source:
def __init__(self, name):
self.name = name
def model(self, time, parameters):
return parameters['m'] * time + parameters['c']
Use in the sampler:
# Somewhere in the initialisation of the sampler
my_source = Source('my_source')
...
# Somewhere in the likelihood
waveform = my_source.model(time)
C) Model is a function
Definition:
def model(time, parameters):
return parameters['m'] * time + parameters['c']
Use in the sampler:
# Somewhere in the initialisation of the sampler
my_source = model # This is a function, not a class instance
...
# Somewhere in the likelihood
waveform = my_source(time, parameters) # Or equivalently, model(time, parameters)
Summary
There is first an argument to be had about whether it should be a class or a function (i.e. A or B vs. C). I'm personally pro class, because it allows for future flexibility. For example, if you need to initialise an approximant or load some data etc.
Second, is the argument between A and B. I believe most of the sources where initially defined as in A (but without an update parameters method). Either will work in practise.
Personally, I think B makes more sense because there is no permanence to the set of parameters - they will get updated on every evaluation of the likelihood. One may then wonder, "if you go with B, why not just make it a function, i.e. C?" To illustrate why not, here is an example
Definition:
class Source:
def __init__(self, name):
self.name = name
self.load_some_data()
def load_some_data(self):
self.data = load('my_big_data_file')
def model(self, time, parameters):
# here you do something with the data, e.g.
m = parameters['m'] * self.data
return m * time + parameters['c']
In this case, making it a function would require you to either load the data everytime you evaluate model
, or load the data and pass it to model
in the likelihood. This would be cumbersome.
TLDR: The argument for classes is it allows flexibility. The argument for B is that parameters are not data that needs to be saved or known by the source
model. However, there could well be data that should be saved by the class, as in the example above.
I'll leave this open to debate and someone else (perhaps the BDFL @paul-lasky ) to decide which. Once decided, I'm happy to implement any of the three for out current models.