LSST Applications g0f08755f38+82efc23009,g12f32b3c4e+e7bdf1200e,g1653933729+a8ce1bb630,g1a0ca8cf93+50eff2b06f,g28da252d5a+52db39f6a5,g2bbee38e9b+37c5a29d61,g2bc492864f+37c5a29d61,g2cdde0e794+c05ff076ad,g3156d2b45e+41e33cbcdc,g347aa1857d+37c5a29d61,g35bb328faa+a8ce1bb630,g3a166c0a6a+37c5a29d61,g3e281a1b8c+fb992f5633,g414038480c+7f03dfc1b0,g41af890bb2+11b950c980,g5fbc88fb19+17cd334064,g6b1c1869cb+12dd639c9a,g781aacb6e4+a8ce1bb630,g80478fca09+72e9651da0,g82479be7b0+04c31367b4,g858d7b2824+82efc23009,g9125e01d80+a8ce1bb630,g9726552aa6+8047e3811d,ga5288a1d22+e532dc0a0b,gae0086650b+a8ce1bb630,gb58c049af0+d64f4d3760,gc28159a63d+37c5a29d61,gcf0d15dbbd+2acd6d4d48,gd7358e8bfb+778a810b6e,gda3e153d99+82efc23009,gda6a2b7d83+2acd6d4d48,gdaeeff99f8+1711a396fd,ge2409df99d+6b12de1076,ge79ae78c31+37c5a29d61,gf0baf85859+d0a5978c5a,gf3967379c6+4954f8c433,gfb92a5be7c+82efc23009,gfec2e1e490+2aaed99252,w.2024.46
LSST Data Management Base Package
Loading...
Searching...
No Matches
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
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 {
59 auto param = std::make_shared<ProperFractionParameterD>();
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}
71FractionalIntegralModel::~FractionalIntegralModel() {};
72
73std::shared_ptr<ProperFractionParameterD> FractionalIntegralModel::at(const Channel &channel) {
74 return _map.at(channel);
75}
76std::shared_ptr<const ProperFractionParameterD> FractionalIntegralModel::at(const Channel &channel) const {
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
102std::vector<std::reference_wrapper<const Channel>> FractionalIntegralModel::get_channels() const {
104 for (auto &datum : _data) rval.emplace_back(datum.first);
105 return rval;
106}
107
108const IntegralModel &FractionalIntegralModel::get_parent_model() const { return *_model; }
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
120FractionalIntegralModel::get_integral_derivative_factors(const Channel &channel) const {
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
164double FractionalIntegralModel::get_integral_remainder(const Channel &channel) const {
165 return (1. - this->at(channel)->get_value()) * _get_integral_remainder(_parent.get(), *_model, channel);
166}
167
168ProperFractionParameterD &FractionalIntegralModel::get_parameter_frac(const Channel &channel) const {
169 return *(_map.at(channel));
170}
171
172ParamRefs &FractionalIntegralModel::get_parameters(ParamRefs &params, ParamFilter *filter) const {
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
184ParamCRefs &FractionalIntegralModel::get_parameters_const(ParamCRefs &params, ParamFilter *filter) const {
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
209std::string FractionalIntegralModel::str() const {
210 std::string s = type_name_str<FractionalIntegralModel>(true) + "(data={";
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;
224 auto parent = std::dynamic_pointer_cast<const FractionalIntegralModel>(model);
225 if (parent == nullptr) return nullptr;
226 bool found = FractionalIntegralModel::_registry.find(*parent) != _registry.end();
227 return found ? parent : nullptr;
228}
229
230std::shared_ptr<FractionalIntegralModel> FractionalIntegralModel::make(
231 std::optional<const Data> data, std::shared_ptr<const IntegralModel> model, bool is_final) {
232 std::shared_ptr<FractionalIntegralModel> ptr = std::make_shared<Shared_enabler>(data, model, is_final);
233 _registry.insert({*ptr, *model});
234 FractionalIntegralModel::_registry_rev.insert({*model, ptr});
235 return ptr;
236}
237
238const std::shared_ptr<const FractionalIntegralModel> FractionalIntegralModel::make_const(
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
char * data
Definition BaseRecord.cc:61
std::uint64_t * ptr
Definition RangeSet.cc:95
T back(T... args)
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.
double get_integral_remainder(const Channel &channel) const
FractionalIntegralModel(const FractionalIntegralModel &)=delete
A Parametric model for the integral of a 2D distribution.
T emplace_back(T... args)
T move(T... args)
double _get_integral_remainder(const FractionalIntegralModel *frac, const IntegralModel &model, const Channel &channel)
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.
STL namespace.
model(data, psfmodels, sources)
T size(T... args)
Options for filtering Parameter instances.
T to_string(T... args)