Loading [MathJax]/extensions/tex2jax.js
LSST Applications g04dff08e69+fafbcb10e2,g0d33ba9806+3d21495239,g0fba68d861+2ea2a6c4b0,g1e78f5e6d3+b3e27b8ebc,g1ec0fe41b4+f536777771,g1fd858c14a+ae46bc2a71,g35bb328faa+fcb1d3bbc8,g4af146b050+9c38a215af,g4d2262a081+154bb484c1,g53246c7159+fcb1d3bbc8,g5a012ec0e7+b20b785ecb,g60b5630c4e+3d21495239,g6273192d42+8013d063df,g67b6fd64d1+4086c0989b,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g7b71ed6315+fcb1d3bbc8,g87b7deb4dc+04106995ce,g8852436030+54b48a5987,g89139ef638+4086c0989b,g9125e01d80+fcb1d3bbc8,g94187f82dc+3d21495239,g989de1cb63+4086c0989b,g9d31334357+3d21495239,g9f33ca652e+83205baa3c,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+85d1f90f4c,gb58c049af0+f03b321e39,gb89ab40317+4086c0989b,gc0bb628dac+d11454dffd,gcf25f946ba+54b48a5987,gd6cbbdb0b4+af3c3595f5,gd9a9a58781+fcb1d3bbc8,gde0f65d7ad+1b29a75088,ge278dab8ac+d65b3c2b70,ge410e46f29+4086c0989b,gf67bdafdda+4086c0989b,v29.0.0.rc6
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
fractionalintegralmodel.cc
Go to the documentation of this file.
1#include <functional>
2#include <memory>
3#include <stdexcept>
4#include <vector>
5
8
15#include "lsst/gauss2d/fit/parameters.h"
17
18namespace lsst::gauss2d::fit {
19
20FractionalIntegralModel::FractionalIntegralModel(std::optional<const Data> data,
21 std::shared_ptr<const IntegralModel> model, bool is_final)
22 : _model(std::move(model)), _parent(_find_parent(_model)), _is_final(is_final) {
23 if (_model == nullptr) throw std::invalid_argument("FractionalIntegralModel model can't be null");
24
25 const auto found = find_model(*_model);
26 if (found != nullptr) {
27 throw std::invalid_argument("FractionalIntegralModel model=" + _model->str()
28 + " already referenced by " + found->str());
29 }
30 if (data) {
31 size_t idx = 0;
32 for (const auto &datum : *data) {
33 const auto &channel = datum.first;
34 const auto &param = datum.second;
35 if (param == nullptr) {
36 throw std::runtime_error("FractionalIntegralModel data[" + channel.get().str()
37 + "] can't be null");
38 } else if (_is_final) {
39 bool is_fixed = param->get_fixed();
40 bool is_one = param->get_value() == 1;
41 std::string errmsg = "";
42 if (!is_fixed) errmsg += " is_fixed != true;";
43 if (!is_one) errmsg += " get_value()=" + std::to_string(param->get_value()) + "!=1;";
44 if (errmsg.size() > 0) {
45 throw std::invalid_argument("FractionalIntegralModel data[" + std::to_string(idx)
46 + "] is_final==true but param for " + channel.get().str()
47 + errmsg);
48 }
49 }
50 if (_map.find(channel) != _map.end()) {
51 throw std::runtime_error("FractionalIntegralModel data[" + std::to_string(idx)
52 + "] channel=" + channel.get().str() + " duplicated");
53 }
54 _data.emplace_back(datum.first, datum.second);
55 _map.insert(_data.back());
56 idx++;
57 }
58 } else {
60 param->set_fixed(_is_final);
61 _data.emplace_back(Channel::NONE(), param);
62 _map.insert(_data.back());
63 }
64 auto data_keys = this->get_channels();
65 auto model_keys = _model.get()->get_channels();
66 if (data_keys != model_keys) {
67 throw std::invalid_argument("FractionalIntegralModel data channels=" + str_iter_ref<true>(data_keys)
68 + " != model.get_channels()=" + str_iter_ref<true>(model_keys));
69 }
70}
72
74 return _map.at(channel);
75}
77 return _map.at(channel);
78}
79
80// https://stackoverflow.com/questions/8147027/
81// how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const/
82// 8147213#comment58654091_25069711
84 template <typename... Args>
85 Shared_enabler(Args &&...args) : FractionalIntegralModel(std::forward<Args>(args)...) {}
86};
87
88typename FractionalIntegralModel::Data::iterator FractionalIntegralModel::begin() noexcept {
89 return _data.begin();
90}
91typename FractionalIntegralModel::Data::const_iterator FractionalIntegralModel::cbegin() const noexcept {
92 return _data.begin();
93}
94
95typename FractionalIntegralModel::Data::iterator FractionalIntegralModel::end() noexcept {
96 return _data.end();
97}
98typename FractionalIntegralModel::Data::const_iterator FractionalIntegralModel::cend() const noexcept {
99 return _data.cend();
100}
101
104 for (auto &datum : _data) rval.emplace_back(datum.first);
105 return rval;
106}
107
109
110inline double _get_integral_remainder(const FractionalIntegralModel *frac, const IntegralModel &model,
111 const Channel &channel) {
112 return (frac == nullptr ? model.get_integral(channel) : frac->get_integral_remainder(channel));
113}
114
115double FractionalIntegralModel::get_integral(const Channel &channel) const {
116 return _map.at(channel)->get_value() * (_get_integral_remainder(_parent.get(), *_model, channel));
117}
118
121 const auto &frac = *(this->_map.at(channel));
122 /*
123 For a model with no parent:
124 gauss2d will evaluate dmodel/dweight_comp
125 Fitters will want dmodel/dfrac
126
127 frac = weight_comp/weight_total
128 dfrac/dweight_comp = 1/weight_total
129 dweight_comp = dfrac*weight_total
130
131 dmodel/d_frac = weight_total*dmodel/dweight_comp
132
133 With a parent:
134
135 parent: weight_comp_parent = weight_total*frac_parent
136 child1: weight_comp_child1 = weight_total*(1 - frac_parent)
137 ...
138 childN: weight_comp_childN = weight_total*(1 - frac_parent)...*(1-frac_childNminus1)
139
140 dmodel/dfrac_parent = weight_total*(1 - frac_parent)*dmodel/dweight_comp
141 = -weight_total*dmodel/dweight_comp
142
143 i.e. there are N fraction parameters dependent on dmodel/dweight_comp for children.
144 (excluding the fixed fraction for an is_final child)
145 */
146 if (_parent == nullptr) {
147 return {{frac, {_model->get_integral(channel), 0., 0.}}};
148 }
149 auto factors = _parent->get_integral_derivative_factors(channel);
150 if (is_final()) {
151 /*
152 The last component's fraction is fixed, but it has an integral of
153 (1 - frac_previous)*integral_remaining, the derivative of which
154 w.r.t. frac_revious is -1*integral_remaining.
155 */
156 factors.back().second[0] *= -1.;
157 } else {
158 // TODO: Check this if/when it's ever enabled
159 factors.push_back({frac, {factors.back().second[0], 0., 0.}});
160 }
161 return factors;
162}
163
165 return (1. - this->at(channel)->get_value()) * _get_integral_remainder(_parent.get(), *_model, channel);
166}
167
169 return *(_map.at(channel));
170}
171
173 _model->get_parameters(params, filter);
174 // Don't return the n_channels fixed frac=1 parameters at the end
175 const size_t n_p_max = _data.size() - (this->is_final() ? this->get_channels().size() : 0);
176 size_t i = 0;
177 for (auto &p : _data) {
178 if (i++ == n_p_max) break;
179 insert_param_channel(p.first, *p.second, params, filter);
180 }
181 return params;
182}
183
185 _model->get_parameters_const(params, filter);
186 const size_t n_p_max = _data.size() - (this->is_final() ? this->get_channels().size() : 0);
187 size_t i = 0;
188 for (const auto &p : _data) {
189 if (i++ == n_p_max) break;
190 insert_param_channel(p.first, *p.second, params, filter);
191 }
192 return params;
193}
194
195bool FractionalIntegralModel::is_final() const { return _is_final; }
196
197size_t FractionalIntegralModel::size() const { return _data.size(); }
198
199std::string FractionalIntegralModel::repr(bool name_keywords, std::string_view namespace_separator) const {
200 std::string s = type_name_str<FractionalIntegralModel>(false, namespace_separator) + "("
201 + (name_keywords ? "data={" : "{");
202 for (const auto &datum : _data) {
203 s += datum.first.get().repr(name_keywords, namespace_separator) + ": "
204 + datum.second->repr(name_keywords, namespace_separator) + ",";
205 }
206 return s + "})";
207}
208
211 for (const auto &datum : _data) {
212 s += datum.first.get().str() + ": " + datum.second->str() + ",";
213 }
214 s += "}, model=" + _model->str() + ", is_final=" + std::to_string(_is_final) + ")";
215 return s;
216}
217
218// Return a pointer to a registered FractionalIntegralModel, if it is one
219// TODO: Verify if this is safe, or if dynamic_cast is better
220// TODO: Consider a second constructor (but it would likely complicate pybind11'ing)
221std::shared_ptr<const FractionalIntegralModel> FractionalIntegralModel::_find_parent(
223 if (model == nullptr) return nullptr;
225 if (parent == nullptr) return nullptr;
226 bool found = FractionalIntegralModel::_registry.find(*parent) != _registry.end();
227 return found ? parent : nullptr;
228}
229
231 std::optional<const Data> data, std::shared_ptr<const IntegralModel> model, bool is_final) {
233 _registry.insert({*ptr, *model});
234 FractionalIntegralModel::_registry_rev.insert({*model, ptr});
235 return ptr;
236}
237
239 std::optional<const Data> data, std::shared_ptr<const IntegralModel> model, bool is_final) {
240 return make(data, model, is_final);
241}
242
243} // namespace lsst::gauss2d::fit
An observational channel, usually representing some range of wavelengths of light.
Definition channel.h:29
An IntegralModel that returns a Parameter-dependent fraction of the flux of another IntegralModel.
ParamCRefs & get_parameters_const(ParamCRefs &params, ParamFilter *filter=nullptr) const override
Same as get_parameters(), but for const refs.
std::string repr(bool name_keywords=false, std::string_view namespace_separator=Object::CC_NAMESPACE_SEPARATOR) const override
Return a full, callable string representation of this.
Data::const_iterator cend() const noexcept
ProperFractionParameterD & get_parameter_frac(const Channel &channel) const
std::string str() const override
Return a brief, human-readable string representation of this.
double get_integral_remainder(const Channel &channel) const
std::vector< std::pair< ParamBaseCRef, ExtraParamFactorValues > > get_integral_derivative_factors(const Channel &channel) const override
Return the partial derivative of the model w.r.t.
ParamRefs & get_parameters(ParamRefs &params, ParamFilter *filter=nullptr) const override
Add Parameter refs matching the filter to a vector, in order.
std::vector< std::reference_wrapper< const Channel > > get_channels() const override
Get the set of channels this instance is applicable for.
double get_integral(const Channel &channel) const override
Get the value of the integral in a single Channel.
static const std::shared_ptr< const FractionalIntegralModel > make_const(std::optional< const Data > data, std::shared_ptr< const IntegralModel > model, bool is_final=false)
Data::const_iterator cbegin() const noexcept
std::shared_ptr< ProperFractionParameterD > at(const Channel &channel)
FractionalIntegralModel(const FractionalIntegralModel &)=delete
static std::shared_ptr< FractionalIntegralModel > make(std::optional< const Data > data, std::shared_ptr< const IntegralModel > model, bool is_final=false)
Construct a FractionalIntegralModel and add to registry.
A Parametric model for the integral of a 2D distribution.
T emplace_back(T... args)
T make_shared(T... args)
T move(T... args)
std::vector< ParamBaseRef > ParamRefs
Definition param_defs.h:13
double _get_integral_remainder(const FractionalIntegralModel *frac, const IntegralModel &model, const Channel &channel)
std::vector< ParamBaseCRef > ParamCRefs
Definition param_defs.h:11
void insert_param_channel(const lsst::gauss2d::fit::Channel &channel, g2f::ParamBase &param, t &params, ParamFilter *filter)
Same as insert_param, but only inserting if filter has a null or matching Channel.
std::string type_name_str(bool strip_namespace=false, std::string_view namespace_str=detail::NAMESPACE_SEPARATOR)
Get a string representation of an arbitrary C++ type, potentially modifying its namespace prefix.
Definition type_name.h:104
STL namespace.
model(data, psfmodels, sources)
T dynamic_pointer_cast(T... args)
T size(T... args)
Options for filtering Parameter instances.
T to_string(T... args)