25#ifndef LSST_GAUSS2D_EVALUATE_H
26#define LSST_GAUSS2D_EVALUATE_H
40namespace lsst::gauss2d {
44enum class BackgroundType :
unsigned char {
48enum class OutputType :
unsigned char {
53enum class GradientType :
unsigned char {
59const size_t N_EXTRA_MAP = 2;
60const size_t N_EXTRA_FACTOR = 3;
61const size_t N_PARAMS_GAUSS2D = 6;
65typedef std::vector<double> vecd;
66typedef std::unique_ptr<vecd> vecdptr;
83inline TermsMoments gaussian_pixel_x_xx(
const double cen_x,
const double x_min,
const double bin_x,
84 const unsigned int dim_x,
const double xx_weight,
85 const double xy_weight) {
86 const double x_init = x_min - cen_x + bin_x / 2.;
87 vecdptr x = std::make_unique<vecd>(dim_x);
88 vecdptr xx = std::make_unique<vecd>(dim_x);
89 vecdptr x_norm = std::make_unique<vecd>(dim_x);
90 for (
unsigned int i = 0; i < dim_x; i++) {
91 double dist = x_init + i * bin_x;
93 (*xx)[i] = dist * dist * xx_weight;
94 (*x_norm)[i] = dist * xy_weight;
96 return TermsMoments({std::move(x), std::move(x_norm), std::move(xx)});
179typedef std::array<double, N_PARAMS_GAUSS2D> Weights;
189Terms terms_from_covar(
double weight,
const Ellipse& ell);
197 double weight_kernel = 0;
199 double xmc_weighted = 0;
200 vecdptr ymc_weighted =
nullptr;
201 double weight_xx = 0;
202 double xmc_sq_norm = 0;
203 vecdptr yy_weighted =
nullptr;
206 explicit TermsPixel(
double weight_i = 0,
double weight_kernel = 0,
double xmc_i = 0,
207 double xmc_weighted_i = 0, vecdptr ymc_weighted_i =
nullptr,
double weight_xx_i = 0,
208 double xmc_sq_norm_i = 0, vecdptr yy_weighted_i =
nullptr)
211 xmc_weighted(xmc_weighted_i),
212 ymc_weighted(std::move(ymc_weighted_i)),
213 weight_xx(weight_xx_i),
214 xmc_sq_norm(xmc_sq_norm_i),
215 yy_weighted(std::move(yy_weighted_i)) {}
218 void set(
double weight_,
double weight_kernel_,
double xmc_,
double xx, vecdptr ymc_weighted_,
219 vecdptr yy_weighted_) {
220 this->weight = weight_;
221 this->weight_kernel = weight_kernel_;
223 this->weight_xx = xx;
224 this->ymc_weighted = std::move(ymc_weighted_);
225 this->yy_weighted = std::move(yy_weighted_);
229typedef std::vector<TermsPixel> TermsPixelVec;
233 vecdptr ymc =
nullptr;
234 double xx_weight = 0;
235 double xy_weight = 0;
236 double yy_weight = 0;
237 double rho_factor = 0;
238 double sig_x_inv = 0;
239 double sig_y_inv = 0;
240 double rho_xy_factor = 0;
241 double sig_x_src_div_conv = 0;
242 double sig_y_src_div_conv = 0;
243 double drho_c_dsig_x_src = 0;
244 double drho_c_dsig_y_src = 0;
245 double drho_c_drho_s = 0;
246 double xmc_t_xy_weight = 0;
248 explicit TermsGradient(vecdptr ymc_i =
nullptr,
double xx_weight_i = 0,
double xy_weight_i = 0,
249 double yy_weight_i = 0,
double rho_factor_i = 0,
double sig_x_inv_i = 0,
250 double sig_y_inv_i = 0,
double rho_xy_factor_i = 0,
251 double sig_x_src_div_conv_i = 0,
double sig_y_src_div_conv_i = 0,
252 double drho_c_dsig_x_src_i = 0,
double drho_c_dsig_y_src_i = 0,
253 double drho_c_drho_s_i = 0,
double xmc_t_xy_weight_i = 0)
254 : ymc(std::move(ymc_i)),
255 xx_weight(xx_weight_i),
256 xy_weight(xy_weight_i),
257 yy_weight(yy_weight_i),
258 rho_factor(rho_factor_i),
259 sig_x_inv(sig_x_inv_i),
260 sig_y_inv(sig_y_inv_i),
261 rho_xy_factor(rho_xy_factor_i),
262 sig_x_src_div_conv(sig_x_src_div_conv_i),
263 sig_y_src_div_conv(sig_y_src_div_conv_i),
264 drho_c_dsig_x_src(drho_c_dsig_x_src_i),
265 drho_c_dsig_y_src(drho_c_dsig_y_src_i),
266 drho_c_drho_s(drho_c_drho_s_i),
267 xmc_t_xy_weight(xmc_t_xy_weight_i) {}
275 this->ymc = std::move(ymc_);
276 const double sig_xy = sig_x * sig_y;
277 this->xx_weight = terms.xx;
278 this->xy_weight = terms.xy;
279 this->yy_weight = terms.yy;
280 this->rho_factor = rho / (1. - rho * rho);
281 this->sig_x_inv = 1 / sig_x;
282 this->sig_y_inv = 1 / sig_y;
283 this->rho_xy_factor = 1. / (1. - rho * rho) / sig_xy;
301 double rho_src = ell_src.
get_rho();
304 double rho_psf = ell_psf.
get_rho();
306 this->sig_x_src_div_conv = sig_x_src / sig_x;
307 this->sig_y_src_div_conv = sig_y_src / sig_y;
309 double covar_psf_dsig_xy = rho_psf * sig_x_psf * sig_y_psf / sig_xy;
312 double sig_p_ratio = sig_x_psf / sig_x;
313 drho = rho_src * this->sig_y_src_div_conv * sig_p_ratio * sig_p_ratio / sig_x
314 - covar_psf_dsig_xy * this->sig_x_src_div_conv / sig_x;
316 this->drho_c_dsig_x_src = drho;
318 double sig_p_ratio = sig_y_psf / sig_y;
319 drho = rho_src * this->sig_x_src_div_conv * sig_p_ratio * sig_p_ratio / sig_y
320 - covar_psf_dsig_xy * this->sig_y_src_div_conv / sig_y;
322 this->drho_c_dsig_y_src = drho;
323 this->drho_c_drho_s = sig_x_src * sig_y_src / sig_xy;
327typedef std::vector<TermsGradient> TermsGradientVec;
329template <
typename t,
class Data,
class Indices>
334 : _param_map(param_map_in), _param_factor(param_factor_in), _output(output) {
335 if(output ==
nullptr) {
336 throw std::invalid_argument(
"GradientsExtra output must not be a nullptr");
338 const auto n_extra_map_rows = param_map_in.get_n_rows();
339 const auto n_extra_fac_rows = param_factor_in.get_n_rows();
340 const auto n_extra_map_cols = param_map_in.get_n_cols();
341 const auto n_extra_fac_cols = param_factor_in.get_n_cols();
342 std::string errmsg =
"";
343 if (n_extra_map_rows != n_gauss) {
344 errmsg +=
"extra_param_map n_rows=" + std::to_string(n_extra_map_rows)
345 +
" != n_gauss=" + std::to_string(n_gauss) +
". ";
347 if (n_extra_fac_rows != n_gauss) {
348 errmsg +=
"extra_param_factor n_rows=" + std::to_string(n_extra_fac_rows)
349 +
" != n_gauss=" + std::to_string(n_gauss) +
". ";
351 if (n_extra_map_cols != N_EXTRA_MAP) {
352 errmsg +=
"extra_param_map n_cols=" + std::to_string(n_extra_map_cols) +
" != 2. ";
354 if (n_extra_fac_cols != N_EXTRA_FACTOR) {
355 errmsg +=
"extra_param_factor n_cols=" + std::to_string(n_extra_fac_cols) +
" != 3. ";
357 if (!errmsg.empty())
throw std::runtime_error(errmsg);
360 GradientsExtra() =
delete;
362 inline void add_index_to_set(
size_t g, std::set<size_t>& set) { set.insert(_param_map.get_value(g, 1)); }
364 inline void add(
size_t g,
size_t dim1,
size_t dim2,
const ValuesGauss& gradients) {
366 _g_check *= (g != 0);
367 const bool to_add = g == _param_map.get_value(_g_check, 0);
368 const auto idx = _param_map.get_value(g, 1);
369 double value = gradients.L * _param_factor.get_value(g, 0)
370 + gradients.sigma_x * _param_factor.get_value(g, 1)
371 + gradients.sigma_y * _param_factor.get_value(g, 2);
372 (*_output)[idx].add_value_unchecked(dim1, dim2, value);
376 std::string
repr(
bool name_keywords =
false,
378 bool is_kw = name_keywords;
380 type_name_str<GradientsExtra<t, Data, Indices>>(
false, namespace_separator) +
"("
381 + (is_kw ?
"param_map=" :
"") + _param_map.repr(is_kw, namespace_separator) +
", "
382 + (is_kw ?
"param_factor=" :
"") + _param_factor.repr(is_kw, namespace_separator) +
", "
383 + (is_kw ?
"output=" :
"") + _output->repr(is_kw, namespace_separator) +
", "
384 + (is_kw ?
"n_gauss=" :
"") + std::to_string(_param_map.get_n_rows()) +
")"
388 std::string
str()
const override {
390 type_name_str<GradientsExtra<t, Data, Indices>>(
true) +
"("
391 +
"param_map=" + _param_map.str() +
", "
392 +
"param_factor=" + _param_factor.str() +
", "
393 +
"output=" + _output->str() +
", "
394 +
"n_gauss=" + std::to_string(_param_map.get_n_rows()) +
")"
402 std::shared_ptr<ImageArray<t, Data>> _output;
407inline void gaussian_pixel(Data& image,
const double weight,
const double cen_x,
const double cen_y,
408 const double xx_weight,
const double yy_weight,
const double xy_weight) {
409 const unsigned int dim_x = image.get_n_cols();
410 const unsigned int dim_y = image.get_n_rows();
411 const auto coordsys = image.get_coordsys();
412 const double x_min = coordsys.x_min;
413 const double y_min = coordsys.y_min;
414 const double bin_x = coordsys.get_dx1() / image.get_n_cols();
415 const double bin_y = coordsys.get_dy2() / image.get_n_rows();
417 const double weight_pix = weight * bin_x * bin_y;
419 const auto moment_terms_y = gaussian_pixel_x_xx(cen_y, y_min, bin_y, dim_y, yy_weight, xy_weight);
420 const vecd& y_weighted = *(moment_terms_y.x_norm);
421 const vecd& yy_weighted = *(moment_terms_y.xx);
423 double x = x_min - cen_x + bin_x / 2.;
425 for (
unsigned int i = 0; i < dim_x; i++) {
426 const double xx_weighted = x * x * xx_weight;
427 for (
unsigned int j = 0; j < dim_y; j++) {
428 image.set_value_unchecked(j, i,
429 weight_pix * exp(-(xx_weighted + yy_weighted[j] - x * y_weighted[j])));
495inline void gaussian_pixel_get_jacobian(
ValuesGauss& out,
const double m,
const double m_unweight,
496 const double xmc_norm,
const double ymc_weighted,
const double xmc,
497 const double ymc,
const double norms_yy,
const double xmc_t_xy_factor,
498 const double sig_x_inv,
const double sig_y_inv,
const double xy_norm,
499 const double xx_norm,
const double yy_weighted,
500 const double rho_div_one_m_rhosq,
const double norms_xy_div_rho,
501 const double dsig_x_conv_src = 1,
const double dsig_y_conv_src = 1,
502 const double drho_c_dsig_x_src = 0,
503 const double drho_c_dsig_y_src = 0,
const double drho_c_drho_s = 1) {
504 out.cen_x = m * (2 * xmc_norm - ymc_weighted);
505 out.cen_y = m * (2 * ymc * norms_yy - xmc_t_xy_factor);
507 const double one_p_xy = 1. + xy_norm;
509 const double dfdrho_c = m
510 * (rho_div_one_m_rhosq * (1 - 2 * (xx_norm + yy_weighted - xy_norm))
511 + xmc * ymc * norms_xy_div_rho);
512 out.sigma_x = dfdrho_c * drho_c_dsig_x_src + dsig_x_conv_src * (m * sig_x_inv * (2 * xx_norm - one_p_xy));
514 = dfdrho_c * drho_c_dsig_y_src + dsig_y_conv_src * (m * sig_y_inv * (2 * yy_weighted - one_p_xy));
516 out.rho = dfdrho_c * drho_c_drho_s;
519inline void gaussian_pixel_get_jacobian_from_terms(
ValuesGauss& out,
const size_t dim1,
522 double m_unweighted,
double xy_norm) {
523 gaussian_pixel_get_jacobian(
524 out, m, m_unweighted * terms_pixel.weight_kernel, terms_pixel.xmc_weighted,
525 (*terms_pixel.ymc_weighted)[dim1], terms_pixel.xmc, (*terms_grad.ymc)[dim1], terms_grad.yy_weight,
526 terms_grad.xmc_t_xy_weight, terms_grad.sig_x_inv, terms_grad.sig_y_inv, xy_norm,
527 terms_pixel.xmc_sq_norm, (*terms_pixel.yy_weighted)[dim1], terms_grad.rho_factor,
528 terms_grad.rho_xy_factor, terms_grad.sig_x_src_div_conv, terms_grad.sig_y_src_div_conv,
529 terms_grad.drho_c_dsig_x_src, terms_grad.drho_c_dsig_y_src, terms_grad.drho_c_drho_s);
533inline void gaussian_pixel_add_values(t& cen_x, t& cen_y, t& L, t& sig_x, t& sig_y, t& rho,
534 const ValuesGauss& values,
const t weight_cen_x = 1,
535 const t weight_cen_y = 1,
const t weight_L = 1,
536 const t weight_sig_x = 1,
const t weight_sig_y = 1,
537 const t weight_rho = 1) {
538 cen_x += weight_cen_x * values.cen_x;
539 cen_y += weight_cen_y * values.cen_y;
540 L += weight_L * values.L;
541 sig_x += weight_sig_x * values.sigma_x;
542 sig_y += weight_sig_y * values.sigma_y;
543 rho += weight_rho * values.rho;
547template <
typename t,
class Data,
class Indices,
bool do_extra>
548inline void gaussians_pixel_add_like_grad(
549 Image<t, Data> * output,
const Image<idx_type, Indices>& grad_param_map,
550 const Image<t, Data>& grad_param_factor,
const size_t N_GAUSS,
551 const std::vector<Weights>& gaussweights,
double& chi_pix,
double& loglike,
const double model,
552 double data,
double sigma_inv,
unsigned int dim1,
unsigned int dim2,
const TermsPixelVec& terms_pixel,
553 const TermsGradientVec& terms_grad,
ValuesGauss& gradients,
554 const std::shared_ptr<
const Image<idx_type, Indices>> extra_param_map,
555 const std::shared_ptr<
const Image<t, Data>> extra_param_factor) {
556 double diff = data - model;
557 chi_pix = sigma_inv * diff;
558 double diffvar = sigma_inv * chi_pix;
559 loglike -= diff * diffvar / 2.;
560 for (
size_t g = 0; g < N_GAUSS; ++g) {
561 const Weights& weights = gaussweights[g];
570 gaussian_pixel_get_jacobian_from_terms(gradients, dim1, terms_pixel[g], terms_grad[g],
571 weights[0] * diffvar, weights[1] * diffvar, weights[2]);
572 gaussian_pixel_add_values<t>(
573 output->_get_value_unchecked(0, grad_param_map.get_value_unchecked(g, 0)),
574 output->_get_value_unchecked(0, grad_param_map.get_value_unchecked(g, 1)),
575 output->_get_value_unchecked(0, grad_param_map.get_value_unchecked(g, 2)),
576 output->_get_value_unchecked(0, grad_param_map.get_value_unchecked(g, 3)),
577 output->_get_value_unchecked(0, grad_param_map.get_value_unchecked(g, 4)),
578 output->_get_value_unchecked(0, grad_param_map.get_value_unchecked(g, 5)), gradients,
579 grad_param_factor.get_value_unchecked(g, 0), grad_param_factor.get_value_unchecked(g, 1),
580 grad_param_factor.get_value_unchecked(g, 2), grad_param_factor.get_value_unchecked(g, 3),
581 grad_param_factor.get_value_unchecked(g, 4), grad_param_factor.get_value_unchecked(g, 5));
582 if constexpr (do_extra) {
583 double value = gradients.L * extra_param_factor->get_value_unchecked(g, 0)
584 + gradients.sigma_x * extra_param_factor->get_value_unchecked(g, 1)
585 + gradients.sigma_y * extra_param_factor->get_value_unchecked(g, 2);
586 output->_get_value_unchecked(0, extra_param_map->get_value_unchecked(g, 1)) += value;
591template <
typename T,
class Data,
class Indices, GradientType gradient_type,
bool do_extra>
592inline T gaussian_pixel_add_all(
size_t g,
size_t j,
size_t i,
double weight,
double sigma_inv,
593 const TermsPixelVec& terms_pixel_vec, ImageArray<T, Data>& output_jac_ref,
594 const Image<idx_type, Indices>& grad_param_map,
595 const Image<T, Data>& grad_param_factor, std::vector<Weights>& gradweights,
596 const TermsGradientVec& terms_grad_vec,
ValuesGauss& gradients,
598 const TermsPixel& terms_pixel = terms_pixel_vec[g];
599 const double xy_norm = terms_pixel.xmc * (*terms_pixel.ymc_weighted)[j];
600 const double value_unweight
601 = terms_pixel.weight * exp(-(terms_pixel.xmc_sq_norm + (*terms_pixel.yy_weighted)[j] - xy_norm));
602 const double value = weight * value_unweight;
604 if constexpr (gradient_type == GradientType::loglike) {
605 Weights& output = gradweights[g];
607 output[1] = value_unweight;
609 }
else if constexpr (gradient_type == GradientType::jacobian) {
610 gaussian_pixel_get_jacobian_from_terms(gradients, j, terms_pixel, terms_grad_vec[g],
611 sigma_inv * value, sigma_inv * value_unweight, xy_norm);
612 gaussian_pixel_add_values<T>(
613 output_jac_ref[grad_param_map.get_value_unchecked(g, 0)]._get_value_unchecked(j, i),
614 output_jac_ref[grad_param_map.get_value_unchecked(g, 1)]._get_value_unchecked(j, i),
615 output_jac_ref[grad_param_map.get_value_unchecked(g, 2)]._get_value_unchecked(j, i),
616 output_jac_ref[grad_param_map.get_value_unchecked(g, 3)]._get_value_unchecked(j, i),
617 output_jac_ref[grad_param_map.get_value_unchecked(g, 4)]._get_value_unchecked(j, i),
618 output_jac_ref[grad_param_map.get_value_unchecked(g, 5)]._get_value_unchecked(j, i),
619 gradients, grad_param_factor.get_value_unchecked(g, 0),
620 grad_param_factor.get_value_unchecked(g, 1), grad_param_factor.get_value_unchecked(g, 2),
621 grad_param_factor.get_value_unchecked(g, 3), grad_param_factor.get_value_unchecked(g, 4),
622 grad_param_factor.get_value_unchecked(g, 5));
623 if constexpr (do_extra) grad_extra->add(g, j, i, gradients);
628template <
typename Indices>
629const std::shared_ptr<const Indices> _param_map_default(
size_t n_gaussians,
630 size_t n_params = N_PARAMS_GAUSS2D,
631 size_t increment = 1) {
632 auto param_map = std::make_shared<Indices>(n_gaussians, n_params);
634 for (
size_t g = 0; g < n_gaussians; ++g) {
635 for (
size_t p = 0; p < n_params; ++p) {
636 param_map->set_value(g, p, index);
640 return std::const_pointer_cast<const Indices>(param_map);
643template <
typename Data>
644const std::shared_ptr<const Data> _param_factor_default(
size_t n_gaussians,
645 size_t n_params = N_PARAMS_GAUSS2D,
647 auto param_factor = std::make_shared<Data>(n_gaussians, n_params);
648 for (
size_t g = 0; g < n_gaussians; ++g) {
649 for (
size_t p = 0; p < n_params; ++p) {
650 param_factor->set_value(g, p, value);
653 return std::const_pointer_cast<const Data>(param_factor);
667template <
typename T,
class Data,
class Indices>
674 GaussianEvaluator(
int x = 0,
const std::shared_ptr<const ConvolvedGaussians> gaussians =
nullptr) {}
704 const std::shared_ptr<ImageArrayT> grads =
nullptr,
706 const std::shared_ptr<
const Image<T, Data>> grad_param_factor =
nullptr,
708 const std::shared_ptr<
const Image<T, Data>> extra_param_factor =
nullptr,
710 : _gaussians(gaussians == nullptr ? GAUSSIANS_NULL : *gaussians),
711 _gaussians_ptr(gaussians == nullptr ? nullptr : gaussians),
712 _n_gaussians(_gaussians.size()),
713 _do_output(output != nullptr),
714 _is_sigma_image((sigma_inv != nullptr) && (sigma_inv->size() > 1)),
715 _do_residual(residual != nullptr),
716 _has_background(background != nullptr),
717 _gradienttype((grads != nullptr && grads->size() > 0)
718 ? (((grads->size() == 1) && ((*grads)[0].get_n_rows() == 1))
719 ? GradientType::loglike
720 : GradientType::jacobian)
721 : GradientType::none),
723 extra_param_map != nullptr && extra_param_factor != nullptr
724 && (_gradienttype == GradientType::loglike || _gradienttype == GradientType::jacobian)),
725 _backgroundtype(background != nullptr ? (_background->size() == 1 ? BackgroundType::constant
726 : BackgroundType::none)
727 : BackgroundType::none),
728 _get_likelihood((data != nullptr) && (sigma_inv != nullptr)),
730 _sigma_inv(sigma_inv),
734 _grad_param_map((_gradienttype == GradientType::none)
736 : ((grad_param_map != nullptr)
738 : detail::_param_map_default<Indices>(_gaussians.size()))),
740 (_gradienttype == GradientType::none)
742 : ((grad_param_factor != nullptr)
744 : detail::_param_factor_default<Data>(_gaussians.size()))),
745 _extra_param_map((_gradienttype == GradientType::none)
747 : ((extra_param_map != nullptr)
749 : detail::_param_map_default<Indices>(_gaussians.size(),
751 _extra_param_factor((_gradienttype == GradientType::none)
753 : ((extra_param_factor != nullptr)
755 : detail::_param_factor_default<Data>(
756 _gaussians.size(), N_EXTRA_FACTOR, 0.))),
757 _grad_extra(_do_extra ? std::make_unique<detail::GradientsExtra<T, Data, Indices>>(
758 *extra_param_map, *extra_param_factor, _grads, _n_gaussians)
760 _grad_param_idx(_grad_param_map == nullptr ? std::vector<size_t>{} : _get_grad_param_idx()),
761 _n_cols(_data ==
nullptr ? (_output ==
nullptr ? (_gradienttype == GradientType::jacobian
762 ? (*_grads)[0].get_n_cols()
764 : _output->get_n_cols())
765 : _data->get_n_cols()),
766 _n_rows(_data ==
nullptr ? (_output ==
nullptr ? (_gradienttype == GradientType::jacobian
767 ? (*_grads)[0].get_n_rows()
769 : _output->get_n_rows())
770 : _data->get_n_rows()),
771 _size(_n_cols * _n_rows),
772 _coordsys(_data ==
nullptr ? (_output ==
nullptr ? (_gradienttype == GradientType::jacobian
773 ? (*_grads)[0].get_coordsys()
775 : _output->get_coordsys())
776 : _data->get_coordsys()) {
777 if (gaussians ==
nullptr)
throw std::runtime_error(
"Gaussians can't be null");
778 if (_has_background && background->size() > 1)
779 throw std::runtime_error(
"Background model size can't be > 1");
780 if (!(_n_cols > 0 && _n_rows > 0))
781 throw std::runtime_error(
"Data can't have n_rows/cols == 0; "
782 + std::to_string(output ==
nullptr));
783 if (!_do_extra && ((extra_param_map !=
nullptr) || (extra_param_factor !=
nullptr))) {
784 throw std::runtime_error(
785 "Must pass all of extra_param_map, extra_param_factor and grads"
786 " to compute extra gradients");
788 if (_get_likelihood) {
790 if (_is_sigma_image) {
791 if (_n_cols != _sigma_inv->get_n_cols() || _n_rows != _sigma_inv->get_n_rows()) {
792 throw std::runtime_error(
"Data matrix dimensions [" + std::to_string(_n_cols) +
','
793 + std::to_string(_n_rows)
794 +
"] don't match inverse variance dimensions ["
795 + std::to_string(_sigma_inv->get_n_cols()) +
','
796 + std::to_string(_sigma_inv->get_n_rows()) +
']');
798 if (_sigma_inv->get_coordsys() != _data->get_coordsys()) {
799 throw std::runtime_error(
"sigma_inv coordsys=" + _sigma_inv->get_coordsys().str()
800 +
"!= coordsys=" + get_coordsys().
str());
803 }
else if ((data !=
nullptr) || (sigma_inv !=
nullptr)) {
804 throw std::runtime_error(
"Passed only one non-null data/sigma_inv");
807 if (_gradienttype != GradientType::none) {
808 const size_t n_gpm = _grad_param_map->get_n_rows();
809 const size_t n_gpf = _grad_param_factor->get_n_rows();
811 if ((n_gpm != _n_gaussians) || (n_gpf != _n_gaussians)) {
812 throw std::invalid_argument(
"nrows for grad_param_map,factor=" + std::to_string(n_gpm) +
","
813 + std::to_string(n_gpf)
814 +
" != n_gaussians=" + std::to_string(_n_gaussians));
817 if ((_grad_param_map->get_n_cols() != N_PARAMS_GAUSS2D)
818 || (_grad_param_factor->get_n_cols() != N_PARAMS_GAUSS2D)) {
819 throw std::invalid_argument(
"n_cols for grad_param_map,factor=" + std::to_string(n_gpm) +
","
820 + std::to_string(n_gpf));
823 const size_t n_grad = (_gradienttype == GradientType::jacobian) ? _grads->size()
824 : (_grads->at(0).get_n_cols());
825 size_t idx_g_max = 0;
826 size_t idx_e_gauss_max = 0;
827 size_t idx_e_param_max = 0;
828 for (
size_t g = 0; g < _n_gaussians; ++g) {
829 for (
size_t p = 0; p < N_PARAMS_GAUSS2D; ++p) {
830 auto value = _grad_param_map->get_value_unchecked(g, p);
831 if (value > idx_g_max) idx_g_max = value;
833 auto value = _extra_param_map->get_value_unchecked(g, 0);
834 if (value > idx_e_gauss_max) idx_e_gauss_max = value;
835 value = _extra_param_map->get_value_unchecked(g, 1);
836 if (value > idx_e_param_max) idx_e_param_max = value;
838 if (!((idx_e_gauss_max < _n_gaussians) && (idx_e_param_max < n_grad))) {
839 throw std::invalid_argument(
" max extra_param_map[0]=" + std::to_string(idx_e_gauss_max)
840 +
" !< n_gaussians=" + std::to_string(_n_gaussians) +
" and/or "
841 +
" max extra_param_map[1]=" + std::to_string(idx_e_param_max)
842 +
" !< n_grad=" + std::to_string(n_grad));
844 if (!(idx_g_max < n_grad)) {
845 throw std::invalid_argument(
"max grad_param_map,extra_param_map[1]="
846 + std::to_string(idx_g_max)
847 +
" !< n_grad=" + std::to_string(n_grad));
850 if (_gradienttype == GradientType::loglike) {
851 if (!_get_likelihood) {
852 throw std::runtime_error(
853 "Can't compute likelihood gradient without computing likelihood;"
854 " did you pass data and sigma_inv arrays?");
856 }
else if (_gradienttype == GradientType::jacobian) {
860 for (
const auto& jac_img : *_grads) {
861 if (!(jac_img->get_n_rows() == _n_rows && jac_img->get_n_cols() == _n_cols)) {
862 throw std::runtime_error(
"grads[0] matrix dimensions [" + std::to_string(_n_cols) +
','
863 + std::to_string(_n_rows)
864 +
"] don't match Jacobian matrix (grads["
865 + std::to_string(idx_jac) +
"]) dimensions ["
866 + std::to_string(jac_img->get_n_cols()) +
','
867 + std::to_string(jac_img->get_n_rows()) +
']');
872 ~GaussianEvaluator()
override =
default;
874 Data
const& IMAGE_NULL_CONST()
const {
return this->_data_null_const; }
875 Indices
const& INDICES_NULL_CONST()
const {
return this->_indices_null_const; }
876 ImageArray<T, Data>
const& IMAGEARRAY_NULL_CONST()
const {
return this->_data_array_null_const; }
878 const CoordinateSystem& get_coordsys()
const {
return _coordsys; }
879 size_t get_n_cols()
const {
return (_n_cols); }
880 size_t get_n_rows()
const {
return (_n_rows); }
881 size_t get_size()
const {
return (_size); }
898 const OutputType output_type
899 = this->_do_output ? (to_add ? OutputType::add : OutputType::overwrite) : OutputType::none;
900 return output_type == OutputType::overwrite
901 ? loglike_gaussians_pixel_output<OutputType::overwrite>()
902 : (output_type == OutputType::add
903 ? loglike_gaussians_pixel_output<OutputType::add>()
904 : loglike_gaussians_pixel_output<OutputType::none>());
907 std::string
repr(
bool name_keywords =
false,
911 auto is_kw = name_keywords;
912 auto name_sep = namespace_separator;
913 std::string c =
", ";
915 type_name_str<GaussianEvaluator>(
false, name_sep) +
"("
916 + (is_kw ?
"gaussians=" :
"") + repr_ptr(_gaussians_ptr, is_kw, name_sep) +
", "
917 + (is_kw ?
"data=" :
"") + repr_ptr(_data, is_kw, name_sep) +
", "
918 + (is_kw ?
"sigma_inv=" :
"") + repr_ptr(_sigma_inv, is_kw, name_sep) +
", "
919 + (is_kw ?
"output=" :
"") + repr_ptr(_output, is_kw, name_sep) +
", "
920 + (is_kw ?
"residual=" :
"") + repr_ptr(_residual, is_kw, name_sep) +
", "
921 + (is_kw ?
"grads=" :
"") + repr_ptr(_grads, is_kw, name_sep) +
", "
922 + (is_kw ?
"grad_param_map=" :
"") + repr_ptr(_grad_param_map, is_kw, name_sep) +
", "
923 + (is_kw ?
"grad_param_factor=" :
"") + repr_ptr(_grad_param_factor, is_kw, name_sep) + c
924 + (is_kw ?
"extra_param_map=" :
"") + repr_ptr(_extra_param_map, is_kw, name_sep) +
", "
925 + (is_kw ?
"extra_param_factor=" :
"") + repr_ptr(_extra_param_factor, is_kw, name_sep) + c
926 + (is_kw ?
"background=" :
"") + repr_ptr(_background, is_kw, name_sep) +
")");
929 std::string
str()
const override {
930 std::string c =
", ";
933 type_name_str<GaussianEvaluator>(
true) +
"("
934 +
"gaussians=" + str_ptr(_gaussians_ptr) + c
935 +
"do_extra=" + std::to_string(_do_extra) + c
936 +
"do_output=" + std::to_string(_do_output) + c
937 +
"do_residual=" + std::to_string(_do_residual) + c
938 +
"has_background=" + std::to_string(_has_background) + c
939 +
"is_sigma_image=" + std::to_string(_is_sigma_image) + c
940 +
"backgroundtype=" + std::to_string(
static_cast<int>(_backgroundtype)) + c
941 +
"get_likelihood=" + std::to_string(_get_likelihood) + c
942 +
"data=" + str_ptr(_data) + c
943 +
"sigma_inv=" + str_ptr(_sigma_inv) + c
944 +
"output=" + str_ptr(_output) + c
945 +
"residual=" + str_ptr(_residual) + c
946 +
"grads=" + str_ptr(_grads) + c
947 +
"grad_param_map=" + str_ptr(_grad_param_map) + c
948 +
"grad_param_factor=" + str_ptr(_grad_param_factor) + c
949 +
"extra_param_map=" + str_ptr(_extra_param_map) + c
950 +
"extra_param_factor=" + str_ptr(_extra_param_factor) + c
951 +
"grad_extra=" + str_ptr(_grad_extra ? _grad_extra.get() :
nullptr) + c
952 +
"grad_param_idx=" + to_string_iter(_grad_param_idx) + c
953 +
"n_cols=" + std::to_string(_n_cols) + c
954 +
"n_rows=" + std::to_string(_n_rows) + c
955 +
"coordsys=" + _coordsys.str()
964 ImageArrayT& IMAGEARRAY_NULL()
const;
967 const std::shared_ptr<const ConvolvedGaussians> _gaussians_ptr;
968 const size_t _n_gaussians;
970 const bool _do_output;
971 const bool _is_sigma_image;
972 const bool _do_residual;
973 const bool _has_background;
974 const GradientType _gradienttype;
975 const bool _do_extra;
976 const BackgroundType _backgroundtype;
977 const bool _get_likelihood;
979 const std::shared_ptr<const DataT> _data;
980 const std::shared_ptr<const DataT> _sigma_inv;
981 const std::shared_ptr<DataT> _output;
982 const std::shared_ptr<DataT> _residual;
983 const std::shared_ptr<ImageArrayT> _grads;
984 const std::shared_ptr<const IndicesT> _grad_param_map;
985 const std::shared_ptr<const DataT> _grad_param_factor;
986 const std::shared_ptr<const IndicesT> _extra_param_map;
987 const std::shared_ptr<const DataT> _extra_param_factor;
988 const std::unique_ptr<detail::GradientsExtra<T, Data, Indices>> _grad_extra;
989 const std::shared_ptr<const DataT> _background;
990 const std::vector<size_t> _grad_param_idx;
991 const size_t _n_cols;
992 const size_t _n_rows;
998 const Data _data_null_const{0, 0};
999 const ImageArray<T, Data> _data_array_null_const{
nullptr};
1000 const Indices _indices_null_const{0, 0};
1002 std::vector<size_t> _get_grad_param_idx()
const {
1003 std::vector<size_t> grad_param_idx;
1004 std::set<size_t> grad_param_idx_uniq;
1005 for (
size_t g = 0; g < _n_gaussians; ++g) {
1006 for (
size_t p = 0; p < N_PARAMS_GAUSS2D; ++p) {
1007 grad_param_idx_uniq.insert(_grad_param_map->get_value(g, p));
1009 if (_do_extra) _grad_extra->add_index_to_set(g, grad_param_idx_uniq);
1011 std::copy(grad_param_idx_uniq.begin(), grad_param_idx_uniq.end(), std::back_inserter(grad_param_idx));
1012 return grad_param_idx;
1019 template <OutputType output_type,
bool getlikelihood, BackgroundType background_type,
bool do_residual,
1020 GradientType gradient_type,
bool do_extra>
1021 double gaussians_pixel_template() {
1022 constexpr bool writeoutput = output_type != OutputType::none;
1023 constexpr bool do_gradient = gradient_type != GradientType::none;
1024 constexpr bool is_loglike = gradient_type == GradientType::loglike;
1026 double background_flat = 0;
1027 if (background_type == BackgroundType::constant) {
1028 background_flat += _background->get_value_unchecked(0, 0);
1030 const size_t n_gaussians = _gaussians.size();
1031 auto data_null = Data{0, 0};
1032 auto array_null = ImageArray<T, Data>{
nullptr};
1035 DataT * output_grad = is_loglike ? &(_grads->at(0)) : nullptr;
1036 ImageArrayT& output_jac_ref = gradient_type == GradientType::jacobian ? (*_grads) : array_null;
1037 size_t grad_param_idx_size = _grad_param_idx.size();
1039 DataT& outputref = writeoutput ? (*_output) : data_null;
1040 DataT& residual_ref = do_residual ? (*_residual) : data_null;
1041 const IndicesT& grad_param_map_ref = do_gradient ? (*_grad_param_map) : INDICES_NULL_CONST();
1042 const DataT& grad_param_factor_ref = do_gradient ? (*_grad_param_factor) : IMAGE_NULL_CONST();
1045 if (_n_cols != _output->get_n_cols() || _n_rows != _output->get_n_rows()) {
1046 throw std::runtime_error(
1047 "Data matrix dimensions [" + std::to_string(_n_cols) +
',' + std::to_string(_n_rows)
1048 +
"] don't match _output matrix dimensions [" + std::to_string(_output->get_n_cols())
1049 +
',' + std::to_string(_output->get_n_rows()) +
']');
1053 const auto& coordsys = this->get_coordsys();
1054 const double x_min = coordsys.get_x_min();
1055 const double y_min = coordsys.get_y_min();
1056 const double bin_x = coordsys.get_dx1();
1057 const double bin_y = coordsys.get_dy2();
1058 const double bin_x_half = bin_x / 2.;
1060 detail::TermsPixelVec terms_pixel(n_gaussians);
1062 const size_t ngaussgrad = n_gaussians * (do_gradient);
1063 detail::TermsGradientVec terms_grad(ngaussgrad);
1064 std::vector<detail::Weights> weights_grad(n_gaussians * (gradient_type == GradientType::loglike));
1066 std::vector<double> weights_conv(n_gaussians);
1068 auto* grad_extra_ptr = do_extra ? _grad_extra.get() :
nullptr;
1070 for (
size_t g = 0; g < n_gaussians; ++g) {
1071 const auto& src = _gaussians[g].get_source();
1072 const auto& kernel = _gaussians[g].get_kernel();
1073 const auto weight_src = src.get_integral_value();
1074 const auto weight_kernel = kernel.get_integral_value();
1076 weights_conv[g] = weight_src * weight_kernel;
1077 const double cen_x = src.get_centroid_const().get_x();
1078 const double cen_y = src.get_centroid_const().get_y();
1079 const auto ell_psf = kernel.get_ellipse_const();
1080 const auto ell_src = src.get_ellipse_const();
1081 const Covariance cov_psf = Covariance(ell_psf);
1082 const Covariance cov_src = Covariance(ell_src);
1084 const auto cov_conv = cov_src.make_convolution(cov_psf);
1085 const auto ell_conv = Ellipse(*cov_conv);
1088 const detail::Terms terms = detail::terms_from_covar(bin_x * bin_y, ell_conv);
1089 auto yvals = detail::gaussian_pixel_x_xx(cen_y, y_min, bin_y, _n_rows, terms.yy, terms.xy);
1091 terms_pixel[g].set(terms.weight, weight_kernel, x_min - cen_x + bin_x_half, terms.xx,
1092 std::move(yvals.x_norm), std::move(yvals.xx));
1094 terms_grad[g].set(terms, ell_src, ell_psf, ell_conv, std::move(yvals.x));
1096 }
catch (
const std::invalid_argument& err) {
1097 throw std::runtime_error(
"Failed to convolve cov_src=" + cov_src.str() +
" with "
1098 + cov_psf.str() +
" due to: " + err.what());
1101 const DataT& data_ref = getlikelihood ? *_data : IMAGE_NULL_CONST();
1102 const DataT& sigma_inv_ref = getlikelihood ? *_sigma_inv : IMAGE_NULL_CONST();
1105 double data_pix = 0;
1106 double sigma_inv_pix = 0;
1108 detail::ValuesGauss gradients = {0, 0, 0, 0, 0, 0};
1110 for (
unsigned int i = 0; i < _n_cols; i++) {
1111 for (
size_t g = 0; g < n_gaussians; ++g) {
1112 terms_pixel[g].xmc_weighted = terms_pixel[g].xmc * terms_pixel[g].weight_xx;
1113 terms_pixel[g].xmc_sq_norm = terms_pixel[g].xmc_weighted * terms_pixel[g].xmc;
1114 if constexpr (gradient_type != GradientType::none) {
1115 terms_grad[g].xmc_t_xy_weight = terms_pixel[g].xmc * terms_grad[g].xy_weight;
1118 for (
unsigned int j = 0; j < _n_rows; j++) {
1119 model = background_flat;
1120 if constexpr (gradient_type == GradientType::jacobian) {
1121 for (
size_t k = 0; k < grad_param_idx_size; ++k) {
1122 output_jac_ref[_grad_param_idx[k]].set_value_unchecked(j, i, 0);
1125 if constexpr (getlikelihood || (gradient_type == GradientType::jacobian)) {
1126 data_pix = data_ref.get_value_unchecked(j, i);
1128 = sigma_inv_ref.get_value_unchecked(j * _is_sigma_image, i * _is_sigma_image);
1130 for (
size_t g = 0; g < n_gaussians; ++g) {
1131 model += detail::gaussian_pixel_add_all<T, Data, Indices, gradient_type, do_extra>(
1132 g, j, i, weights_conv[g], sigma_inv_pix, terms_pixel, output_jac_ref,
1133 grad_param_map_ref, grad_param_factor_ref, weights_grad, terms_grad, gradients,
1136 if constexpr (output_type == OutputType::overwrite) {
1137 outputref.set_value_unchecked(j, i, model);
1138 }
else if constexpr (output_type == OutputType::add) {
1139 outputref.add_value_unchecked(j, i, model);
1141 if constexpr (getlikelihood) {
1142 static_assert(getlikelihood);
1143 if constexpr ((gradient_type == GradientType::none)
1144 || (gradient_type == GradientType::jacobian)) {
1145 chi_pix = (data_pix - model) * sigma_inv_pix;
1146 loglike -= chi_pix * chi_pix / 2.;
1149 else if constexpr (gradient_type == GradientType::loglike) {
1150 detail::gaussians_pixel_add_like_grad<T, Data, Indices, do_extra>(
1151 output_grad, grad_param_map_ref, grad_param_factor_ref, n_gaussians,
1152 weights_grad, chi_pix, loglike, model, data_pix, sigma_inv_pix, j, i,
1153 terms_pixel, terms_grad, gradients, _extra_param_map, _extra_param_factor);
1156 if constexpr (do_residual) residual_ref.set_value_unchecked(j, i, chi_pix);
1158 for (
size_t g = 0; g < n_gaussians; ++g) terms_pixel[g].xmc += bin_x;
1164 template <OutputType output_type,
bool get_likelihood, BackgroundType background_type,
bool do_residual,
1166 double loglike_gaussians_pixel_getlike() {
1172 return (_gradienttype == GradientType::loglike)
1173 ? gaussians_pixel_template<output_type, get_likelihood, background_type, do_residual,
1174 GradientType::loglike, do_extra>()
1175 : (_gradienttype == GradientType::jacobian
1176 ? gaussians_pixel_template<output_type, get_likelihood, background_type,
1177 do_residual, GradientType::jacobian, do_extra>()
1178 : gaussians_pixel_template<output_type, get_likelihood, background_type,
1179 do_residual, GradientType::none, do_extra>());
1183 template <OutputType output_type,
bool get_likelihood, BackgroundType background_type>
1184 double loglike_gaussians_pixel_extra() {
1189 return _do_residual ? (_do_extra ? loglike_gaussians_pixel_getlike<output_type, get_likelihood,
1190 background_type,
true,
true>()
1191 : loglike_gaussians_pixel_getlike<output_type, get_likelihood,
1192 background_type,
true,
false>())
1193 : (_do_extra ? loglike_gaussians_pixel_getlike<output_type, get_likelihood,
1194 background_type, false, true>()
1195 : loglike_gaussians_pixel_getlike<output_type, get_likelihood,
1196 background_type, false, false>());
1200 template <OutputType output_type>
1201 double loglike_gaussians_pixel_output() {
1206 return _get_likelihood
1208 ? loglike_gaussians_pixel_extra<output_type,
true,
1209 BackgroundType::constant>()
1210 : loglike_gaussians_pixel_extra<output_type, true, BackgroundType::none>())
1211 : (_has_background ? loglike_gaussians_pixel_extra<output_type, false,
1212 BackgroundType::constant>()
1213 : loglike_gaussians_pixel_extra<output_type, false,
1214 BackgroundType::none>());
1232template <
typename T,
class Data,
class Indices>
1233std::shared_ptr<Data> make_gaussians_pixel(
const std::shared_ptr<const ConvolvedGaussians> gaussians,
1234 std::shared_ptr<Data> output =
nullptr,
1235 const unsigned int n_rows = 0,
const unsigned int n_cols = 0,
1236 const std::shared_ptr<const CoordinateSystem> coordsys =
nullptr,
1237 bool to_add =
false) {
1238 if (output ==
nullptr) {
1240 throw std::invalid_argument(
"Cannot set to_add if output is nullptr");
1242 output = std::make_shared<Data>(n_rows, n_cols,
nullptr, coordsys);
1245 = std::make_unique<GaussianEvaluator<T, Data, Indices>>(gaussians,
nullptr,
nullptr, output);
1246 evaluator->loglike_pixel(to_add);
Definition gaussian.h:249
A coordinate system specifying image scale and orientation.
Definition coordinatesystem.h:43
An Ellipse with sigma_x, sigma_y, and rho values.
Definition ellipse.h:283
double get_rho() const override
Get rho.
Definition ellipse.cc:319
double get_sigma_x() const override
Get sigma_x.
Definition ellipse.cc:320
double get_sigma_y() const override
Get sigma_y.
Definition ellipse.cc:321
std::string repr(bool name_keywords=false, std::string_view namespace_separator=Object::CC_NAMESPACE_SEPARATOR) const override
Definition evaluate.h:907
std::string str() const override
Return a brief, human-readable string representation of this.
Definition evaluate.h:929
double loglike_pixel(bool to_add=false)
Definition evaluate.h:897
GaussianEvaluator(const std::shared_ptr< const ConvolvedGaussians > gaussians, const std::shared_ptr< const Image< T, Data > > data=nullptr, const std::shared_ptr< const Image< T, Data > > sigma_inv=nullptr, const std::shared_ptr< Image< T, Data > > output=nullptr, const std::shared_ptr< Image< T, Data > > residual=nullptr, const std::shared_ptr< ImageArrayT > grads=nullptr, const std::shared_ptr< const Image< idx_type, Indices > > grad_param_map=nullptr, const std::shared_ptr< const Image< T, Data > > grad_param_factor=nullptr, const std::shared_ptr< const Image< idx_type, Indices > > extra_param_map=nullptr, const std::shared_ptr< const Image< T, Data > > extra_param_factor=nullptr, const std::shared_ptr< const Image< T, Data > > background=nullptr)
Construct a GaussianEvaluator, inferring outputs from inputs.
Definition evaluate.h:699
An array of compatible Images.
Definition image.h:268
static constexpr std::string_view CC_NAMESPACE_SEPARATOR
The C++ namespace separator.
Definition object.h:45
Definition evaluate.h:231
Definition evaluate.h:194
Definition evaluate.h:182