Skip to content

Commit

Permalink
API/UI to reestimate model parameter initial values
Browse files Browse the repository at this point in the history
  • Loading branch information
kecnry committed Dec 29, 2022
1 parent 3f7e9c2 commit 0eeb211
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 5 deletions.
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ New Features

- Linear1D model component now estimates slope and intercept. [#1947]

- Model fitting: API and UI to re-estimate model parameters based on current data/subset selection.
[#1952]

Cubeviz
^^^^^^^

Expand Down
2 changes: 2 additions & 0 deletions jdaviz/components/tooltip.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ const tooltips = {
'plugin-plot-options-mixed-state': 'Current values are mixed, click to sync at shown value',
'plugin-model-fitting-add-model': 'Create model component',
'plugin-model-fitting-param-fixed': 'Check the box to freeze parameter value',
'plugin-model-fitting-reestimate-all': 'Re-estimate initial values based on the current data/subset selection for all free parameters',
'plugin-model-fitting-reestimate': 'Re-estimate initial values based on the current data/subset selection for all free parameters in this component',
'plugin-unit-conversion-apply': 'Apply unit conversion',
'plugin-line-lists-load': 'Load list into "Loaded Lines" section of plugin',
'plugin-line-lists-plot-all-in-list': 'Plot all lines in this list',
Expand Down
61 changes: 56 additions & 5 deletions jdaviz/configs/default/plugins/model_fitting/model_fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class ModelFitting(PluginTemplateMixin, DatasetSelectMixin,
* :meth:`model_components`
* :meth:`get_model_component`
* :meth:`set_model_component`
* :meth:`reestimate_model_parameters`
* ``equation`` (:class:`~jdaviz.core.template_mixin.AutoTextField`)
* ``cube_fit``
Only exposed for Cubeviz. Whether to fit the model to the cube instead of to the
Expand Down Expand Up @@ -161,7 +162,7 @@ def user_api(self):
expose += ['spatial_subset']
expose += ['spectral_subset', 'model_component', 'poly_order', 'model_component_label',
'model_components', 'create_model_component', 'remove_model_component',
'get_model_component', 'set_model_component',
'get_model_component', 'set_model_component', 'reestimate_model_parameters',
'equation', 'add_results', 'residuals_calculate', 'residuals']
if self.config == "cubeviz":
expose += ['cube_fit']
Expand Down Expand Up @@ -433,6 +434,13 @@ def create_model_component(self, model_component=None, model_component_label=Non
if comp_label in [cm['id'] for cm in self.component_models]:
raise ValueError(f"model component label '{comp_label}' already in use")

new_model = self._initialize_model_component(model_comp, comp_label, poly_order=poly_order)
self.component_models = self.component_models + [new_model]
# update the default label (likely adding the suffix)
self._update_comp_label_default()
self._update_model_equation_default()

def _initialize_model_component(self, model_comp, comp_label, poly_order=None):
new_model = {"id": comp_label, "model_type": model_comp,
"parameters": [], "model_kwargs": {}}
model_cls = MODELS[model_comp]
Expand Down Expand Up @@ -486,10 +494,7 @@ def create_model_component(self, model_component=None, model_component_label=Non
self._initialized_models[comp_label] = initialized_model

new_model["Initialized"] = True
self.component_models = self.component_models + [new_model]
# update the default label (likely adding the suffix)
self._update_comp_label_default()
self._update_model_equation_default()
return new_model

def remove_model_component(self, model_component_label):
"""
Expand Down Expand Up @@ -581,6 +586,52 @@ def set_model_component(self, model_component_label, parameter, value=None, fixe

return parameter

def vue_reestimate_model_parameters(self, model_component_label=None, **kwargs):
self.reestimate_model_parameters(model_component_label=model_component_label)

def reestimate_model_parameters(self, model_component_label=None):
"""
Re-estimate all free parameters in a given model component given the currently selected
data and subset selections.
Parameters
----------
model_component_label : str or None.
The label given to the existing model component. If None, will iterate over all model
components.
"""
if model_component_label is None:
ret = []
for model_comp in self.component_models:
ret.append(self.reestimate_model_parameters(model_comp["id"]))
return ret

try:
model_index, model_comp = [(i, x) for i, x in enumerate(self.component_models)
if x["id"] == model_component_label][0]
except IndexError:
raise ValueError(f"'{model_component_label}' is not a label of an existing model component") # noqa

# store user-fixed parameters so we can revert after re-initializing
fixed_params = {p['name']: p for p in model_comp['parameters'] if p['fixed']}

new_model = self._initialize_model_component(model_comp['model_type'],
model_component_label,
poly_order=model_comp['model_kwargs'].get('degree', None)) # noqa

# revert fixed parameters to user-value
new_model['parameters'] = [fixed_params.get(p['name'], p) for p in new_model['parameters']]

# reset traitlet so vue picks up on changes by adding (and then removing) an empty entry,
# this avoids resetting the open state of the accordion in the UI.
component_models = self.component_models
component_models[model_index] = new_model
self.component_models = self.component_models + [{}]
self.component_models = self.component_models[:-1]

# return user-friendly info on revised model
return self.get_model_component(model_component_label)

@property
def model_components(self):
"""
Expand Down
34 changes: 34 additions & 0 deletions jdaviz/configs/default/plugins/model_fitting/model_fitting.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@

<div v-if="component_models.length">
<j-plugin-section-header>Model Parameters</j-plugin-section-header>
<v-row justify="end">
<j-tooltip tipid='plugin-model-fitting-reestimate-all'>
<v-btn
tile
:elevation=0
x-small
dense
color="turquoise"
dark
style="padding-left: 8px; padding-right: 6px;"
@click="reestimate_model_parameters">
<v-icon left small dense style="margin-right: 2px">mdi-restart</v-icon>
Re-estimate free parameters
</v-btn>
</j-tooltip>
</v-row>
<v-row>
<v-expansion-panels accordion>
<v-expansion-panel
Expand Down Expand Up @@ -113,6 +129,24 @@
>
<span><b>{{ item.id }}</b> model component not in equation</span>
</v-row>
<v-row justify="end"
style="padding-top: 12px; padding-right: 2px"
>
<j-tooltip tipid='plugin-model-fitting-reestimate'>
<v-btn
tile
:elevation=0
x-small
dense
color="turquoise"
dark
style="padding-left: 8px; padding-right: 6px;"
@click="reestimate_model_parameters(item.id)">
<v-icon left small dense style="margin-right: 2px">mdi-restart</v-icon>
Re-estimate free parameters
</v-btn>
</j-tooltip>
</v-row>
<v-div
v-for="param in item.parameters"
:style="componentInEquation(item.id) ? '': 'opacity: 0.3'"
Expand Down

0 comments on commit 0eeb211

Please sign in to comment.