Open millernb opened 2 years ago
I see. That is indeed relevant.
I take it you also want to generally have different priors for different meta config values (I mean for shared parameters), right?
The way I would probably go for now, which seems to be on the lines of the dictionary you are implementing right now, would be:
y
-dictionary where each key corresponds to a specific meta configlsqfitgui
s meta config setup on runtime but rather "roll-out" all config values into keys such that they are "static" (unless you want to have some meta-meta configs)In other words
from itertools import product
n_exps = range(2, 6)
t_mins = range(0, 5)
PRIOR_KEYS = ["a", "E"]
def make_prior(n, t_min, key):
return gv.gvar(...)
y = {(n, t_min): Y_DATA[t_min:] for n, t_min in product(n_exps, t_mins)} # assuming Y_DATA is an array
x = {(n, t_min): X_DATA[t_min:] for n, t_min in product(n_exps, t_mins)} # assuming X_DATA is an array
prior = {(n, t_min, key): make_prior(n, t_min, key) for n, t_min, key in product(n_exps, t_mins, PRIOR_KEYS)}
def fcn(x, p):
out = {}
for (n, t_min), xx in x.itmes():
pp = {key: p[(n, t_min, key)] for key in PRIOR_KEYS}
out[(n, t_min)] = ... # some logic here
return out
def stability_plot(fit, **kwargs):
fig = ...
return fig
fit = nonlinear_fit((x, y), fcn=fcn, prior=prior)
gui = FitGUI(fit)
gui.plots.append({"name": "Stability plot", "fcn": stability_plot, "fcn_kwargs": {...}})
Is this also what you have in mind?
However, this makes caching a bit harder as the extensive computational part is now on the callbacks (is this already meaningful, here?). And, a priori, they do not know which part to re-run and which one does not change the final result. Right now, without having a custom wrapper for the expansive call (namely nonlinear_fit
), like the MultiFitterModel
provides, I find it quite hard to come up with a general caching model. Happy for suggestionns :)
I'm starting to realize that this might be a bit harder than I originally thought. Even though meta_config
contains enough information to uniquely the determine stability plots, actually implementing this feature is going to require an API rewrite.
Using FitsDict
(or something akin to it) faces an obvious scope problem. Ultimately we want to access the FitsDict
object inside content.py:get_figures
since that's where the stability plot will be generate, which means replacing the fit
argument with the FitsDict
object and a unique key specifying the fit (prior
+ meta
). Tracing backwards from get_figures
, that means we'll also need to replace the fit
arg in
content.py:get_content
content.py:get_layout
dashboard.py:update_layout_from_meta
?dashboard.py:update_layout_from_prior
?...and probably other places, too.
A useful starting point might be merging process_meta
, process_prior
, and fit_setup_function
. Something like:
def process_fit(fits_dict, prior_flat=None, prior=None, meta_array=None):
meta_config = fits_dict.meta_config
prior = ... # process prior
meta_args = .... # process meta_args
fit_key = ... # generate key from prior, meta_args
return fits_dict[fit_key]
with most of the real processing happening inside FitsDict._make_fit
.
Import edge case to keep in mind: meta_config
not specified (eg, run_server(fit)
)
Alternatively, we could just give up on caching fits -- I suspect we'll create orders of magnitude more fits than we'll retrieve, so the gains might be pretty minor anyway. (But if we're generating many fits, we might want to make liberal use of gv.switch_gvar
to prevent slowdowns.)
To make the stability plots, we'll still need access to meta_config
and fit_setup_function
(and fit_setup_kwargs
?) inside content.py:get_figures
, however.
Or we could just extend this block of code.
# from dashboard.py:get_layout
sidebar = get_sidebar(fit.prior, meta_config=meta_config, meta_values=meta_values)
sidebar.className = "sticky-top bg-light p-4"
content = get_content(fit, name=name, plots=plots) if use_default_content else None
additional_content = get_additional_content(fit) if get_additional_content else None
layout = html.Div(
children=html.Div(
children=[
html.Div(
children=sidebar,
className="col-xs-12 col-sm-5 col-md-4 col-xl-3 col-xxl-2",
id="sticky-sidebar",
),
html.Div(
children=[content, additional_content],
className="col-xs-12 col-sm-7 col-md-8 col-xl-9 col-xxl-10",
),
],
className="row py-3",
),
className="container-fluid",
)
For instance, children=[content, additional_content] -> children=[content, additional_content, stability_plots]
with another function similar to content.py:get_content
except solely responsible for creating the stability plots.
In this case, I'd argue stability plots are additional content :)
So get_additional_content
would provide the stability plot html. But see also the suggestion in #16
Yup, additional_content
is probably the right place for now. I think it's possible to have good defaults such that we could automatically generate stability plots from meta_config
, but we should probably write more examples employing meta_config
before we attempt to generalize (eg, a multikey example would be nice).
Leaving this issue open as the current draft is not yet discussed & finalized.
Thinking forward to correlator fits: we would eventually like to be able to make stability plots where we vary the (1) number of states, (2) the starting time, and (3) the ending time. Fortunately all of these variables can be specified in the
meta_args
, as well as their minimum and maximum values.(Ideally we'd like to make stability plots like those in Fig 10 of hep-lat/2011.12166, but I doubt we could generalize this behavior to any
lsqfit.nonlinear_fit
object.)Some ideas on how we could implement this feature:
stability_param='E0'
)Additional goals:
fit1.y == fit2.y
)Cached fit dictionary (haven't tested this, but you get the idea; also, doesn't account for updating priors!)