LSST Applications g0d97872fb5+4fd969bb9d,g1653933729+34a971ddd9,g28da252d5a+072f89fe25,g2bbee38e9b+a99b0ab4cd,g2bc492864f+a99b0ab4cd,g2ca4be77d2+c0e3b27cd8,g2cdde0e794+704103fe75,g3156d2b45e+6e87dc994a,g347aa1857d+a99b0ab4cd,g35bb328faa+34a971ddd9,g3a166c0a6a+a99b0ab4cd,g3e281a1b8c+8ec26ec694,g4005a62e65+ba0306790b,g414038480c+9ed5ed841a,g569e0e2b34+cb4faa46ad,g5a97de2502+520531a62c,g717e5f8c0f+29153700a5,g7ede599f99+367733290c,g80478fca09+17051a22cc,g82479be7b0+f2f1ea0a87,g858d7b2824+29153700a5,g8b782ad322+29153700a5,g8cd86fa7b1+05420e7f7d,g9125e01d80+34a971ddd9,ga5288a1d22+e7f674aaf3,gae0086650b+34a971ddd9,gae74b0b5c6+45ef5cdc51,gb58c049af0+ace264a4f2,gc28159a63d+a99b0ab4cd,gcf0d15dbbd+8051a81198,gda6a2b7d83+8051a81198,gdaeeff99f8+7774323b41,gdf4d240d4a+34a971ddd9,ge2409df99d+cb167bac99,ge33fd446bb+29153700a5,ge79ae78c31+a99b0ab4cd,gf0baf85859+890af219f9,gf5289d68f6+9faa5c5784,w.2024.36
LSST Data Management Base Package
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Static Public Member Functions | Static Public Attributes | List of all members
lsst::gauss2d::fit::Model< T, Image, Indices, Mask > Class Template Reference

A collection of Sources comprising a ParametricModel of Data. More...

#include <model.h>

Inheritance diagram for lsst::gauss2d::fit::Model< T, Image, Indices, Mask >:
lsst::gauss2d::fit::ParametricModel lsst::gauss2d::fit::Parametric lsst::gauss2d::Object

Public Types

typedef GaussianEvaluator< T, Image, Indices > Evaluator
 
typedef std::map< std::reference_wrapper< const Channel >, std::vector< std::unique_ptr< const Gaussians > > > GaussiansMap
 
typedef Data< T, Image, MaskModelData
 
typedef ModelData::Observation Observation
 

Public Member Functions

 Model (std::shared_ptr< const ModelData > data, PsfModels &psfmodels, Sources &sources, Priors &priors)
 Construct a Model instance from Data, PsfModels, Sources and Priors.
 
void add_extra_param_map (const Channel &channel, ExtraParamMap &map_extra, const GradParamMap &map_grad, ParameterMap &offsets) const override
 Add extra Parameter indices to a map.
 
void add_extra_param_factors (const Channel &channel, ExtraParamFactors &factors) const override
 Add extra Parameter gradient factors to an existing vector.
 
void add_grad_param_map (const Channel &channel, GradParamMap &map, ParameterMap &offsets) const override
 Add Parameter gradient indices to an existing map.
 
void add_grad_param_factors (const Channel &channel, GradParamFactors &factors) const override
 Add Parameter gradient factors to an existing map.
 
std::vector< double > compute_loglike_grad (bool include_prior=true, bool print=false, bool verify=false, double findiff_frac=1e-5, double findiff_add=1e-5, double rtol=1e-3, double atol=1e-8)
 Compute the gradient (partial first derivative) of the log-likelihood for each free parameter.
 
std::unique_ptr< Imagecompute_hessian (bool transformed=false, bool include_prior=true, std::optional< HessianOptions > options=std::nullopt, bool print=false)
 Compute the Hessian matrix (second order partial derivatives) of the log likehood.
 
std::vector< double > evaluate (bool print=false, bool normalize_loglike=false)
 Evaluate the model for every Observation in _data.
 
double evaluate_observation (size_t idx)
 Evaluate a single observation with the given index in _data.
 
std::shared_ptr< const ModelDataget_data () const
 Return _data.
 
EvaluatorMode get_mode () const
 
std::unique_ptr< const lsst::gauss2d::Gaussiansget_gaussians (const Channel &channel) const override
 Return the vector of Gaussian sub-components controlled by this model.
 
std::vector< double > get_loglike_const_terms ()
 Get the constant (variance-dependent) terms of the log likelihood for each observation.
 
size_t get_n_gaussians (const Channel &channel) const override
 Return the number of Gaussian sub-components controlled by this model.
 
std::vector< std::shared_ptr< Image > > get_outputs () const
 Return _outputs (output Image instances for each Observation in _data)
 
std::vector< std::pair< ParamBaseCRef, size_t > > get_offsets_parameters () const
 
ParamRefsget_parameters (ParamRefs &params, ParamFilter *filter=nullptr) const override
 Add Parameter refs matching the filter to a vector, in order.
 
ParamCRefsget_parameters_const (ParamCRefs &params, ParamFilter *filter=nullptr) const override
 Same as get_parameters(), but for const refs.
 
ParamRefsget_parameters_observation (ParamRefs &params, size_t idx, ParamFilter *filter=nullptr) const
 Same as get_parameters(), but for a single Observation with index idx in _data.
 
ParamCRefsget_parameters_observation_const (ParamCRefs &params, size_t idx, ParamFilter *filter=nullptr) const
 Same as get_parameters_const(), but for a single Observation with index idx in _data.
 
Priors get_priors () const
 Return _priors, the list of Prior instances.
 
PsfModels get_psfmodels () const
 Return _psfmodels, the list of PsfModel instances for each Observation in _data.
 
Sources get_sources () const
 Return _sources, the list of Source instances for each Observation in _data.
 
void set_extra_param_factors (const Channel &channel, ExtraParamFactors &factors, size_t index) const override
 Set extra Parameter gradient factors in an existing map.
 
void set_grad_param_factors (const Channel &channel, GradParamFactors &factors, size_t index) const override
 Set Parameter gradient factors in an existing map.
 
void setup_evaluators (EvaluatorMode mode=EvaluatorMode::image, std::vector< std::vector< std::shared_ptr< Image > > > outputs={}, std::vector< std::shared_ptr< Image > > residuals={}, std::vector< std::shared_ptr< Image > > outputs_prior={}, std::shared_ptr< Image > residuals_prior=nullptr, bool force=false, bool print=false)
 Setup Evaluator instances for every Observation in _data using the given EvaluatorMode.
 
size_t size () const
 Get the size of this->_data.
 
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.
 
std::string str () const override
 Return a brief, human-readable string representation of this.
 
std::vector< std::stringverify_jacobian (double findiff_frac=1e-5, double findiff_add=1e-5, double rtol=1e-3, double atol=1e-3, double max_ll_diff=0)
 Verify that the Jacobian is correct by comparing to finite differences.
 
ParamRefs get_parameters_new (ParamFilter *filter=nullptr) const
 Same as get_parameters(), but returning a new vector.
 
ParamCRefs get_parameters_const_new (ParamFilter *filter=nullptr) const
 Same as get_parameters_const(), but returning a new vector.
 

Static Public Member Functions

static std::string_view null_str (const std::string_view &namespace_separator)
 

Static Public Attributes

static constexpr std::string_view CC_NAMESPACE_SEPARATOR = "::"
 The C++ namespace separator.
 
static constexpr std::string_view NULL_STR_GENERAL = "None"
 
static constexpr std::string_view PY_NAMESPACE_SEPARATOR = "."
 

Detailed Description

template<typename T, typename Image, typename Indices, typename Mask>
class lsst::gauss2d::fit::Model< T, Image, Indices, Mask >

A collection of Sources comprising a ParametricModel of Data.

Template Parameters
TThe type of the Image (usually float or double).
ImageThe class of image used in Data.
IndicesThe class of index map used in Data.
MaskThe class of mask used in Data.

Definition at line 78 of file model.h.

Member Typedef Documentation

◆ Evaluator

template<typename T , typename Image , typename Indices , typename Mask >
typedef GaussianEvaluator<T, Image, Indices> lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::Evaluator

Definition at line 80 of file model.h.

◆ GaussiansMap

template<typename T , typename Image , typename Indices , typename Mask >
typedef std::map<std::reference_wrapper<const Channel>, std::vector<std::unique_ptr<const Gaussians> > > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::GaussiansMap

Definition at line 82 of file model.h.

◆ ModelData

template<typename T , typename Image , typename Indices , typename Mask >
typedef Data<T, Image, Mask> lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::ModelData

Definition at line 83 of file model.h.

◆ Observation

template<typename T , typename Image , typename Indices , typename Mask >
typedef ModelData::Observation lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::Observation

Definition at line 84 of file model.h.

Constructor & Destructor Documentation

◆ Model()

template<typename T , typename Image , typename Indices , typename Mask >
lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::Model ( std::shared_ptr< const ModelData > data,
PsfModels & psfmodels,
Sources & sources,
Priors & priors )
inlineexplicit

Construct a Model instance from Data, PsfModels, Sources and Priors.

Parameters
dataThe data to model.
psfmodelsA vector of PSF models, ordered to match each Observation in data.
sourcesA vector of Source models.
priorsA vector of Prior likelihoods.

Definition at line 94 of file model.h.

96 : _data(std::move(data)),
97 _size(_data == nullptr ? 0 : _data->size()),
98 _size_priors(priors.size()) {
99 if (_data == nullptr) throw std::invalid_argument("Model data can't be null");
100 size_t size = this->size();
101 if (psfmodels.size() != size) {
102 throw std::invalid_argument("Model psfmodels.size()=" + std::to_string(psfmodels.size())
103 + "!= data.size()=" + std::to_string(size));
104 }
105
106 _outputs.resize(size);
107 _psfmodels.reserve(size);
108 size_t i = 0;
109 for (auto& psfmodel : psfmodels) {
110 if (psfmodel == nullptr)
111 throw std::invalid_argument("Model psfmodels[" + std::to_string(i) + "] can't be null");
112 _psfcomps.push_back(psfmodel->get_gaussians());
113 _psfmodels.push_back(psfmodel);
114 i++;
115 }
116
117 _priors.reserve(_size_priors);
118 i = 0;
119 for (auto& prior : priors) {
120 if (prior == nullptr)
121 throw std::invalid_argument("Model priors[" + std::to_string(i) + "] can't be null");
122 _priors.push_back(prior);
123 i++;
124 }
125
126 _sources.reserve(sources.size());
127 i = 0;
128 for (auto& source : sources) {
129 if (source == nullptr)
130 throw std::invalid_argument("Model Sources[" + std::to_string(i) + "] can't be null");
131 _sources.push_back(source);
132 i++;
133 }
134
135 _gaussians_srcs = this->_get_gaussians_srcs();
136 _factors_extra_in.resize(_size);
137 _factors_grad_in.resize(_size);
138 _map_extra_in.resize(_size);
139 _map_grad_in.resize(_size);
140 }
char * data
Definition BaseRecord.cc:61
size_t size() const
Get the size of this->_data.
Definition model.h:1375
T move(T... args)
psfmodels(data)
Definition test_model.py:33
T push_back(T... args)
T reserve(T... args)
T resize(T... args)
T to_string(T... args)

Member Function Documentation

◆ add_extra_param_factors()

template<typename T , typename Image , typename Indices , typename Mask >
void lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::add_extra_param_factors ( const Channel & channel,
ExtraParamFactors & factors ) const
inlineoverridevirtual

Add extra Parameter gradient factors to an existing vector.

Parameters
channelThe Channel to add factors for.
factorsThe ExtraParamFactors to add to.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 681 of file model.h.

681 {
682 for (auto& source : _sources) source->add_extra_param_factors(channel, factors);
683 }
void add_extra_param_factors(const Channel &channel, ExtraParamFactors &factors) const override
Add extra Parameter gradient factors to an existing vector.
Definition model.h:681
const char * source()
Source function that allows astChannel to source from a Stream.
Definition Stream.h:224

◆ add_extra_param_map()

template<typename T , typename Image , typename Indices , typename Mask >
void lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::add_extra_param_map ( const Channel & channel,
ExtraParamMap & map_extra,
const GradParamMap & map_grad,
ParameterMap & offsets ) const
inlineoverridevirtual

Add extra Parameter indices to a map.

Parameters
channelThe Channel to add indices for.
map_extraThe ExtraParamMap to add to.
map_gradThe completed GradParamMap.
offsetsA map of index offsets for Parameters that have already been added.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 677 of file model.h.

678 {
679 for (auto& source : _sources) source->add_extra_param_map(channel, map_extra, map_grad, offsets);
680 }
void add_extra_param_map(const Channel &channel, ExtraParamMap &map_extra, const GradParamMap &map_grad, ParameterMap &offsets) const override
Add extra Parameter indices to a map.
Definition model.h:677

◆ add_grad_param_factors()

template<typename T , typename Image , typename Indices , typename Mask >
void lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::add_grad_param_factors ( const Channel & channel,
GradParamFactors & factors ) const
inlineoverridevirtual

Add Parameter gradient factors to an existing map.

Parameters
channelThe Channel to add factors for.
factorsThe GradParamFactors to add to.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 687 of file model.h.

687 {
688 for (auto& source : _sources) source->add_grad_param_factors(channel, factors);
689 }
void add_grad_param_factors(const Channel &channel, GradParamFactors &factors) const override
Add Parameter gradient factors to an existing map.
Definition model.h:687

◆ add_grad_param_map()

template<typename T , typename Image , typename Indices , typename Mask >
void lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::add_grad_param_map ( const Channel & channel,
GradParamMap & map,
ParameterMap & offsets ) const
inlineoverridevirtual

Add Parameter gradient indices to an existing map.

Parameters
channelThe Channel to add indices for.
mapThe map to add to.
offsetsA map of index offsets for Parameters that have already been added.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 684 of file model.h.

684 {
685 for (auto& source : _sources) source->add_grad_param_map(channel, map, offsets);
686 }
void add_grad_param_map(const Channel &channel, GradParamMap &map, ParameterMap &offsets) const override
Add Parameter gradient indices to an existing map.
Definition model.h:684

◆ compute_hessian()

template<typename T , typename Image , typename Indices , typename Mask >
std::unique_ptr< Image > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::compute_hessian ( bool transformed = false,
bool include_prior = true,
std::optional< HessianOptions > options = std::nullopt,
bool print = false )
inline

Compute the Hessian matrix (second order partial derivatives) of the log likehood.

Parameters
transformedWhether the matrix should be computed for transformed parameters or not If not, parameter transforms are temporarily removed.
include_priorWhether to include the prior likelihood(s) in the Hessian.
optionsOptions for computing the Hessian via finite differencing of loglikelihood gradients. If null, the Hessian is estimated as J^T J (where J is the Jacobian).
Returns
The Hessian matrix for all free parameters.

Definition at line 825 of file model.h.

827 {
828 this->setup_evaluators(EvaluatorMode::loglike_grad, {}, {}, {}, nullptr, true, print);
829
830 auto filter_free = g2f::ParamFilter{false, true, true, true};
831 auto params_free = this->get_parameters_new(&filter_free);
832 params_free = nonconsecutive_unique<ParamBaseRef>(params_free);
833 const size_t n_params_free = params_free.size();
834
836 transforms.reserve(n_params_free);
837 if (!transformed) {
838 for (auto& paramref : params_free) {
839 auto& param = paramref.get();
840 transforms.emplace_back(param.get_transform_ptr());
841 double value = param.get_value();
842 param.set_transform(nullptr);
843 if (param.get_value() != value) {
844 throw std::logic_error("Param " + param.str() + " changed value from "
845 + std::to_string(value) + " after dropping transform");
846 }
847 }
848 }
849
850 auto hessian = std::make_unique<Image>(n_params_free, n_params_free);
851 const auto n_obs = this->size();
852 std::vector<size_t> param_idx(n_params_free);
853
854 size_t n_integral = 0;
855 size_t n_frac = 0;
856
857 for (size_t idx_param = 0; idx_param < n_params_free; idx_param++) {
858 const auto param = params_free[idx_param];
859 const auto found = _offsets_params.find(param);
860 if (found == _offsets_params.end()) {
861 throw std::runtime_error(param.get().str() + " not found in _offsets_params\n"
862 + ERRMSG_PARAMS);
863 }
864 param_idx[idx_param] = (*found).second;
865 if (not options) {
866 const auto name = param.get().get_name();
867 n_integral += name == IntegralParameterD::_name;
868 n_frac += name == ProperFractionParameterD::_name;
869 }
870 }
871
872 if (options) {
873 const HessianOptions& opt = *options;
874
875 auto loglike_grad = this->compute_loglike_grad(include_prior, false, false);
876
877 size_t idx_param = 0;
878 for (auto& paramref : params_free) {
879 auto& param = paramref.get();
880 const double value_init = param.get_value();
881 const double value_transformed = param.get_value_transformed();
882 // Make the finite differencing go away from the possible peak likelihood
883 // This is probably a good idea even if return_negative isn't necessary
884 double diff = std::copysign(std::abs(value_transformed * opt.findiff_frac) + opt.findiff_add,
885 -loglike_grad[idx_param]);
886 diff = finite_difference_param(param, diff);
887 double diffabs = std::abs(diff);
888 this->evaluate();
889
890 auto loglike_grad2 = this->compute_loglike_grad(include_prior, false, false);
891 for (size_t idx_col = 0; idx_col < n_params_free; ++idx_col) {
892 double dll_dx = loglike_grad2[idx_col] - loglike_grad[idx_col];
893 dll_dx *= opt.return_negative ? (1 - 2 * (dll_dx > 0)) / diffabs : 1 / diff;
894 hessian->set_value_unchecked(idx_param, idx_col, dll_dx);
895 }
896 param.set_value(value_init);
897 idx_param++;
898 }
899 } else {
900 if ((n_integral > 0) && (n_frac > 0)) {
901 throw std::runtime_error(
902 "Cannot compute_hessian without options for model with free Integral and "
903 "ProperFraction "
904 "parameters (this is a bug); please supply options to use finite differencing.");
905 }
906
908 this->evaluate();
909
910 hessian->fill(0);
911
912 for (size_t idx_obs = 0; idx_obs < n_obs; ++idx_obs) {
913 const auto& jacs = this->_grads.at(idx_obs);
914 for (size_t idx_param1 = 0; idx_param1 < n_params_free; ++idx_param1) {
915 const auto& jac1 = jacs->at(param_idx[idx_param1]);
916 const size_t n_rows = jac1.get_n_rows();
917 const size_t n_cols = jac1.get_n_cols();
918
919 for (size_t idx_param2 = 0; idx_param2 < n_params_free; ++idx_param2) {
920 const auto& jac2 = jacs->at(param_idx[idx_param2]);
921 const size_t n_rows2 = jac2.get_n_rows();
922 const size_t n_cols2 = jac2.get_n_cols();
923
924 if ((n_rows != n_rows2) || (n_cols != n_cols2)) {
925 throw std::logic_error("n_rows,n_cols=" + to_string_float(n_rows) + ","
926 + to_string_float(n_cols)
927 + "!= n_rows2,n_cols2=" + to_string_float(n_rows2) + ","
928 + to_string_float(n_cols2));
929 }
930 double dx = 0;
931 for (size_t row = 0; row < n_rows; ++row) {
932 for (size_t col = 0; col < n_cols; ++col) {
933 // Note the sign convention of negative diagonal values
934 // at maximum likelihood
935 dx -= jac1.get_value(row, col) * jac2.get_value(row, col);
936 }
937 }
938 if (print) {
939 std::cout << "calling hessian->add_value(" << idx_param1 << "," << idx_param2
940 << "," << dx << ")" << std::endl;
941 }
942 hessian->add_value(idx_param1, idx_param2, dx);
943 }
944 }
945 }
946 if (include_prior && (_priors.size() > 0)) {
947 std::map<ParamBaseCRef, size_t> param_idx_map = {};
948 for (size_t idx_param = 0; idx_param < n_params_free; idx_param++) {
949 const auto param = params_free[idx_param];
950 if (_offsets_params.find(param) == _offsets_params.end()) {
951 throw std::runtime_error(param.get().str() + " not found in _offsets_params\n"
952 + ERRMSG_PARAMS);
953 }
954 param_idx_map[param] = idx_param;
955 }
956
957 size_t prior_size = 0;
958 for (const auto& prior : _priors) {
959 prior_size += prior->size();
960 }
961 auto dll_prior = std::make_unique<Image>(n_params_free, prior_size);
962 dll_prior->fill(0);
963
964 size_t idx_prior = 0;
965 for (const auto& prior : _priors) {
966 auto result = prior->evaluate(true);
967
968 for (const auto& [param_cref, values_base] : result.jacobians) {
969 size_t idx_residual = 0;
970 size_t idx_param = param_idx_map[param_cref];
971 for (const auto& value_base : values_base) {
972 dll_prior->add_value(idx_param, idx_prior + idx_residual++, value_base);
973 }
974 }
975 idx_prior += prior->size();
976 }
977
978 for (size_t row = 0; row < n_params_free; ++row) {
979 for (size_t row2 = 0; row2 < n_params_free; ++row2) {
980 double dll_dx = 0;
981 for (size_t col = 0; col < prior_size; ++col) {
982 dll_dx -= dll_prior->get_value(row, col) * dll_prior->get_value(row2, col);
983 }
984 hessian->add_value(row, row2, dll_dx);
985 if (print) {
986 std::cout << "calling hessian->add_value(" << row << "," << row2 << "," << dll_dx
987 << ") for prior" << std::endl;
988 }
989 }
990 }
991 }
992 }
993
994 if (!transformed) {
995 size_t idx_param = 0;
996 for (auto& paramref : params_free) {
997 auto& param = paramref.get();
998 double value = param.get_value();
999 param.set_transform(transforms[idx_param]);
1000 if (param.get_value() != value) {
1001 throw std::logic_error("Param " + param.str() + " changed value from "
1002 + std::to_string(value) + " after dropping transform");
1003 }
1004 idx_param++;
1005 }
1006 }
1007
1008 return hessian;
1009 }
py::object result
Definition _schema.cc:429
void setup_evaluators(EvaluatorMode mode=EvaluatorMode::image, std::vector< std::vector< std::shared_ptr< Image > > > outputs={}, std::vector< std::shared_ptr< Image > > residuals={}, std::vector< std::shared_ptr< Image > > outputs_prior={}, std::shared_ptr< Image > residuals_prior=nullptr, bool force=false, bool print=false)
Setup Evaluator instances for every Observation in _data using the given EvaluatorMode.
Definition model.h:1199
std::vector< double > evaluate(bool print=false, bool normalize_loglike=false)
Evaluate the model for every Observation in _data.
Definition model.h:1019
std::vector< double > compute_loglike_grad(bool include_prior=true, bool print=false, bool verify=false, double findiff_frac=1e-5, double findiff_add=1e-5, double rtol=1e-3, double atol=1e-8)
Compute the gradient (partial first derivative) of the log-likelihood for each free parameter.
Definition model.h:704
ParamRefs get_parameters_new(ParamFilter *filter=nullptr) const
Same as get_parameters(), but returning a new vector.
Definition parametric.h:27
T copysign(T... args)
T end(T... args)
T endl(T... args)
T find(T... args)
double finite_difference_param(ParamBase &param, double delta)
Definition param_defs.cc:6
@ jacobian
Compute the model Jacobian.
@ loglike_grad
Compute the gradients of the log likelihood.
std::string to_string_float(const T value, const int precision=6, const bool scientific=true)
Definition to_string.h:15
T size(T... args)
int row
Definition CR.cc:145
int col
Definition CR.cc:144
static const std::string _name
Definition parameters.h:63
Options for filtering Parameter instances.

◆ compute_loglike_grad()

template<typename T , typename Image , typename Indices , typename Mask >
std::vector< double > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::compute_loglike_grad ( bool include_prior = true,
bool print = false,
bool verify = false,
double findiff_frac = 1e-5,
double findiff_add = 1e-5,
double rtol = 1e-3,
double atol = 1e-8 )
inline

Compute the gradient (partial first derivative) of the log-likelihood for each free parameter.

Parameters
include_priorWhether to include the prior likelihood(s) in the gradients.
printWhether to print diagnostic/debugging information.
verifyWhether to verify the values by comparing to finite differences.
findiff_fracThe value of the finite difference increment, as a fraction of the parameter value.
findiff_addThe minimum value of the finite difference increment.
rtolThe allowed relative tolerance in the Jacobian as compared to the finite difference.
atolThe allowed absolute tolerance in the Jacobian as compared to the finite difference.
Returns
The gradient of the log likelihoods for the free parameters of the model, in the order returned by get_parameters.

Definition at line 704 of file model.h.

707 {
708 this->setup_evaluators(EvaluatorMode::loglike_grad, {}, {}, {}, nullptr, true, print);
709 this->evaluate(print);
710
711 auto filter_free = g2f::ParamFilter{false, true, true, true};
712 auto params_free = this->get_parameters_const_new(&filter_free);
713 params_free = nonconsecutive_unique<ParamBaseCRef>(params_free);
714
715 const size_t n_params = this->_offsets_params.size();
716 if (n_params != params_free.size()) {
717 throw std::runtime_error("_offsets_params=" + str_map_ref<true>(_offsets_params)
718 + ".size() = " + std::to_string(n_params)
719 + " != params_free=" + str_iter_ref<true>(params_free) + ".size() + 1 ="
720 + std::to_string(params_free.size()) + "; " + ERRMSG_PARAMS);
721 }
722 std::map<ParamBaseCRef, size_t> param_idx = {};
723 for (size_t idx_param = 0; idx_param < n_params; idx_param++) {
724 const auto param = params_free[idx_param];
725 if (_offsets_params.find(param) == _offsets_params.end()) {
726 throw std::runtime_error(param.get().str() + " not found in _offsets_params\n"
727 + ERRMSG_PARAMS);
728 }
729 param_idx[param] = idx_param;
730 }
731 if (print) {
732 std::cout << "params_free=" << str_iter_ref<true>(params_free) << std::endl;
733 std::cout << "_offsets_params=" << str_map_ref<true>(_offsets_params) << std::endl;
734 std::cout << "param_idx=" << str_map_ref<true>(param_idx) << std::endl;
735 }
736 std::vector<double> loglike_grads(n_params, 0.);
737
738 size_t idx_obs = 0;
739 for (const auto& grads_obs : _grads) {
740 const auto& values = grads_obs->at(0);
741 const size_t n_cols = values.get_n_cols();
742 if (n_cols != n_params + 1) {
743 throw std::logic_error(this->_data->at(idx_obs).get().str()
744 + " loglike_grads n_cols=" + std::to_string(n_cols)
745 + " != n_params (from offsets) = " + std::to_string(n_params + 1));
746 }
747 for (const auto& [paramref, col] : _offsets_params) {
748 loglike_grads.at(param_idx[paramref]) += values.get_value(0, col);
749 }
750 idx_obs++;
751 }
752 if (include_prior) {
753 for (const auto& prior : _priors) {
754 auto result = prior->evaluate(true);
755
756 // TODO: Confirm that this works with ShapePrior
757 for (const auto& paramref : params_free) {
758 double dll_dx = result.compute_dloglike_dx(paramref);
759 loglike_grads.at(param_idx[paramref]) += dll_dx;
760 }
761 }
762 }
763 if (verify) {
764 std::string errmsg;
765 this->setup_evaluators(g2f::EvaluatorMode::loglike);
766 auto loglike = this->evaluate();
767
768 auto params = this->get_parameters_new(&filter_free);
769 params = nonconsecutive_unique<ParamBaseRef>(params);
770
771 for (size_t idx_param = 0; idx_param < n_params; idx_param++) {
772 auto& param = params[idx_param].get();
773 double value = param.get_value_transformed();
774 double diff = std::copysign(std::abs(value * findiff_frac) + findiff_add,
775 loglike_grads[idx_param]);
776 diff = finite_difference_param(param, diff);
777 std::vector<double> loglike_new_plus;
778 std::vector<double> loglike_new_minus;
779 try {
780 param.set_value_transformed(value + diff / 2.);
781 loglike_new_plus = this->evaluate();
782 param.set_value_transformed(value - diff / 2.);
783 loglike_new_minus = this->evaluate();
784 } catch (std::runtime_error& e) {
785 param.set_value_transformed(value + diff);
786 loglike_new_plus = this->evaluate();
787 loglike_new_minus = loglike;
788 }
789 auto dloglike = (sum_iter(loglike_new_plus) - sum_iter(loglike_new_minus)) / diff;
790 auto close = isclose(dloglike, loglike_grads[idx_param], rtol, atol);
791 param.set_value_transformed(value);
792 if (!close.isclose) {
793 double dll_dx_exact = loglike_grads[idx_param];
794 double dlp_dx_sum = 0;
795 for (const auto& prior : _priors) {
796 double dlp_dx = prior->evaluate(true).compute_dloglike_dx(param, true);
797 dlp_dx_sum += dlp_dx;
798 }
799 double dlp_dx_findiff = (loglike_new_plus.back() - loglike_new_minus.back()) / diff;
800 errmsg += param.str() + " failed loglike_grad verification; isclose=" + close.str()
801 + " from findiff=" + to_string_float(dloglike) + " vs "
802 + to_string_float(dll_dx_exact)
803 + " (diff = " + to_string_float(dll_dx_exact - dloglike)
804 + " , ratio = " + to_string_float(dll_dx_exact / dloglike)
805 + "); dll_dx_prior=" + to_string_float(dlp_dx_sum)
806 + " vs findiff: " + to_string_float(dlp_dx_findiff) + "\n";
807 }
808 }
809 if (errmsg.size() > 0) throw std::logic_error(errmsg);
810 }
811
812 return loglike_grads;
813 }
T back(T... args)
ParamCRefs get_parameters_const_new(ParamFilter *filter=nullptr) const
Same as get_parameters_const(), but returning a new vector.
Definition parametric.h:33
IsCloseResult< T > isclose(T a, T b, T rtol=1e-5, T atol=1e-8)
Check if two values are close, within some tolerances.
Definition util.h:48
@ loglike
Compute the log likelihood only.
Value sum_iter(const Container< Value > &container)
Definition math.h:23

◆ evaluate()

template<typename T , typename Image , typename Indices , typename Mask >
std::vector< double > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::evaluate ( bool print = false,
bool normalize_loglike = false )
inline

Evaluate the model for every Observation in _data.

Parameters
printWhether to print diagnostic statements to stdout.
normalize_loglikeWhether to include the normalizing (variance-dependent) term in the log likelihood. If false, the log likelihood for a model with no residuals is 0.
Returns
The log likelihood of each observation, followed by the summed log likelihood of the priors.

Definition at line 1019 of file model.h.

1019 {
1020 if (!_is_setup) throw std::runtime_error("Can't call evaluate before setup_evaluators");
1021
1022 std::vector<double> result(_size + 1);
1023 bool is_loglike_grad = this->_mode == EvaluatorMode::loglike_grad;
1024
1025 if (is_loglike_grad) {
1026 // loglike_grad adds values, and they have to be reset to zero first
1027 // This must be done for all observations in this case, because
1028 // most parameters change grads in all observations
1029 for (size_t idx = 0; idx < _size; ++idx) {
1030 this->_grads[idx]->at(0).fill(0);
1031 }
1032 }
1033
1034 for (size_t idx = 0; idx < _size; ++idx) {
1035 result[idx] = this->_evaluate_observation(idx, print, is_loglike_grad);
1036 }
1037 if ((this->_mode == EvaluatorMode::loglike) || is_loglike_grad
1038 || (this->_mode == EvaluatorMode::loglike_image) || (this->_mode == EvaluatorMode::jacobian)) {
1039 result[_size] = this->_evaluate_priors(print, normalize_loglike);
1040 }
1041
1042 return result;
1043 }
@ loglike_image
Compute the model images and log likelihood.

◆ evaluate_observation()

template<typename T , typename Image , typename Indices , typename Mask >
double lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::evaluate_observation ( size_t idx)
inline

Evaluate a single observation with the given index in _data.

Parameters
idxThe numeric index of the observation.
Returns
The log likelihood of the model for that observation.

Definition at line 1051 of file model.h.

1051 {
1052 _check_obs_idx(idx);
1053 return _evaluate_observation(_evaluators[idx]);
1054 }

◆ get_data()

template<typename T , typename Image , typename Indices , typename Mask >
std::shared_ptr< const ModelData > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_data ( ) const
inline

Return _data.

Definition at line 1057 of file model.h.

1057{ return _data; }

◆ get_gaussians()

template<typename T , typename Image , typename Indices , typename Mask >
std::unique_ptr< const lsst::gauss2d::Gaussians > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_gaussians ( const Channel & channel) const
inlineoverridevirtual

Return the vector of Gaussian sub-components controlled by this model.

Parameters
channelThe Channel to return Gaussians for.
Returns
The Gaussians controlled by this model.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 1061 of file model.h.

1061 {
1063 // TODO: Rethink this; it's not sufficient unless sources are single component gaussians
1064 in.reserve(_sources.size());
1065
1066 for (auto& source : _sources) {
1067 auto data = source->get_gaussians(channel)->get_data();
1068 in.emplace_back(data);
1069 }
1070
1071 return std::make_unique<lsst::gauss2d::Gaussians>(in);
1072 }

◆ get_loglike_const_terms()

template<typename T , typename Image , typename Indices , typename Mask >
std::vector< double > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_loglike_const_terms ( )
inline

Get the constant (variance-dependent) terms of the log likelihood for each observation.

Returns
A vector with the constant terms for each observation, and a final term for the priors.

Definition at line 1079 of file model.h.

1079 {
1080 const size_t n_data = this->size();
1081 if (this->_likelihood_const_terms.empty()) {
1082 this->_likelihood_const_terms.resize(n_data + 1);
1083
1084 for (size_t idx = 0; idx < n_data; ++idx) {
1085 const auto& sigma_inv = this->_data[idx].get_sigma_inverse();
1086 double loglike = 0;
1087 const size_t n_rows = sigma_inv.get_n_rows();
1088 const size_t n_cols = sigma_inv.get_n_cols();
1089 for (size_t col = 0; col < n_cols; ++col) {
1090 for (size_t row = 0; row < n_rows; ++row) {
1091 loglike += LOG_1 - log(sigma_inv._get_value(row, col));
1092 }
1093 }
1094 _likelihood_const_terms[idx] = loglike;
1095 }
1096 }
1097 double loglike = 0;
1098 for (const auto& prior : this->_priors) {
1099 auto values = prior->get_loglike_const_terms();
1100 for (const auto value : values) loglike += value;
1101 }
1102 _likelihood_const_terms[n_data] = loglike;
1103 return _likelihood_const_terms;
1104 }
T empty(T... args)
const double LOG_1
Definition math.h:8

◆ get_mode()

template<typename T , typename Image , typename Indices , typename Mask >
EvaluatorMode lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_mode ( ) const
inline

Definition at line 1059 of file model.h.

1059{ return _mode; }

◆ get_n_gaussians()

template<typename T , typename Image , typename Indices , typename Mask >
size_t lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_n_gaussians ( const Channel & channel) const
inlineoverridevirtual

Return the number of Gaussian sub-components controlled by this model.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 1106 of file model.h.

1106 {
1107 size_t n_g = 0;
1108 for (auto& source : _sources) n_g += source->get_n_gaussians(channel);
1109 return n_g;
1110 }
size_t get_n_gaussians(const Channel &channel) const override
Return the number of Gaussian sub-components controlled by this model.
Definition model.h:1106

◆ get_offsets_parameters()

template<typename T , typename Image , typename Indices , typename Mask >
std::vector< std::pair< ParamBaseCRef, size_t > > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_offsets_parameters ( ) const
inline

Definition at line 1115 of file model.h.

1115 {
1117 for (auto& [param, offset] : _offsets_params) {
1118 offsets.emplace_back(param, offset);
1119 }
1120 return offsets;
1121 }

◆ get_outputs()

template<typename T , typename Image , typename Indices , typename Mask >
std::vector< std::shared_ptr< Image > > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_outputs ( ) const
inline

Return _outputs (output Image instances for each Observation in _data)

Definition at line 1113 of file model.h.

1113{ return _outputs; }

◆ get_parameters()

template<typename T , typename Image , typename Indices , typename Mask >
ParamRefs & lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_parameters ( ParamRefs & params,
ParamFilter * filter = nullptr ) const
inlineoverridevirtual

Add Parameter refs matching the filter to a vector, in order.

Parameters
paramsThe vector to add to.
filterThe filter to apply to this Object's parameters.
Returns
A ref to params (for method chaining)

Implements lsst::gauss2d::fit::Parametric.

Definition at line 1123 of file model.h.

1123 {
1124 for (auto& source : _sources) source->get_parameters(params, filter);
1125 for (auto& psfmodel : _psfmodels) psfmodel->get_parameters(params, filter);
1126 _data->get_parameters(params, filter);
1127 return params;
1128 }
ParamRefs & get_parameters(ParamRefs &params, ParamFilter *filter=nullptr) const override
Add Parameter refs matching the filter to a vector, in order.
Definition model.h:1123

◆ get_parameters_const()

template<typename T , typename Image , typename Indices , typename Mask >
ParamCRefs & lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_parameters_const ( ParamCRefs & params,
ParamFilter * filter = nullptr ) const
inlineoverridevirtual

Same as get_parameters(), but for const refs.

Implements lsst::gauss2d::fit::Parametric.

Definition at line 1130 of file model.h.

1130 {
1131 for (auto& source : _sources) source->get_parameters_const(params, filter);
1132 for (auto& psfmodel : _psfmodels) psfmodel->get_parameters_const(params, filter);
1133 _data->get_parameters_const(params, filter);
1134 return params;
1135 }
ParamCRefs & get_parameters_const(ParamCRefs &params, ParamFilter *filter=nullptr) const override
Same as get_parameters(), but for const refs.
Definition model.h:1130

◆ get_parameters_const_new()

ParamCRefs lsst::gauss2d::fit::Parametric::get_parameters_const_new ( ParamFilter * filter = nullptr) const
inlineinherited

Same as get_parameters_const(), but returning a new vector.

Definition at line 33 of file parametric.h.

33 {
34 ParamCRefs params{};
35 get_parameters_const(params, filter);
36 return params;
37 }
virtual ParamCRefs & get_parameters_const(ParamCRefs &params, ParamFilter *filter=nullptr) const =0
Same as get_parameters(), but for const refs.
std::vector< ParamBaseCRef > ParamCRefs
Definition param_defs.h:11

◆ get_parameters_new()

ParamRefs lsst::gauss2d::fit::Parametric::get_parameters_new ( ParamFilter * filter = nullptr) const
inlineinherited

Same as get_parameters(), but returning a new vector.

Definition at line 27 of file parametric.h.

27 {
28 ParamRefs params{};
29 get_parameters(params, filter);
30 return params;
31 }
virtual ParamRefs & get_parameters(ParamRefs &params, ParamFilter *filter=nullptr) const =0
Add Parameter refs matching the filter to a vector, in order.
std::vector< ParamBaseRef > ParamRefs
Definition param_defs.h:13

◆ get_parameters_observation()

template<typename T , typename Image , typename Indices , typename Mask >
ParamRefs & lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_parameters_observation ( ParamRefs & params,
size_t idx,
ParamFilter * filter = nullptr ) const
inline

Same as get_parameters(), but for a single Observation with index idx in _data.

Definition at line 1138 of file model.h.

1139 {
1140 _check_obs_idx(idx);
1141 for (auto& source : _sources) source->get_parameters(params, filter);
1142 _psfmodels[idx]->get_parameters(params, filter);
1143 _data->at(idx).get().get_parameters(params, filter);
1144 return params;
1145 }

◆ get_parameters_observation_const()

template<typename T , typename Image , typename Indices , typename Mask >
ParamCRefs & lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_parameters_observation_const ( ParamCRefs & params,
size_t idx,
ParamFilter * filter = nullptr ) const
inline

Same as get_parameters_const(), but for a single Observation with index idx in _data.

Definition at line 1148 of file model.h.

1149 {
1150 _check_obs_idx(idx);
1151 for (auto& source : _sources) source->get_parameters_const(params, filter);
1152 _psfmodels[idx]->get_parameters_const(params, filter);
1153 _data->at(idx).get().get_parameters_const(params, filter);
1154 return params;
1155 }

◆ get_priors()

template<typename T , typename Image , typename Indices , typename Mask >
Priors lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_priors ( ) const
inline

Return _priors, the list of Prior instances.

Definition at line 1158 of file model.h.

1158{ return _priors; }

◆ get_psfmodels()

template<typename T , typename Image , typename Indices , typename Mask >
PsfModels lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_psfmodels ( ) const
inline

Return _psfmodels, the list of PsfModel instances for each Observation in _data.

Definition at line 1161 of file model.h.

1161{ return _psfmodels; }

◆ get_sources()

template<typename T , typename Image , typename Indices , typename Mask >
Sources lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::get_sources ( ) const
inline

Return _sources, the list of Source instances for each Observation in _data.

Definition at line 1164 of file model.h.

1164{ return _sources; }

◆ null_str()

static std::string_view lsst::gauss2d::Object::null_str ( const std::string_view & namespace_separator)
inlinestaticinherited

Definition at line 49 of file object.h.

49 {
50 return namespace_separator == CC_NAMESPACE_SEPARATOR ? "nullptr" : NULL_STR_GENERAL;
51 }
static constexpr std::string_view CC_NAMESPACE_SEPARATOR
The C++ namespace separator.
Definition object.h:45
static constexpr std::string_view NULL_STR_GENERAL
Definition object.h:46

◆ repr()

template<typename T , typename Image , typename Indices , typename Mask >
std::string lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::repr ( bool name_keywords = false,
std::string_view namespace_separator = Object::CC_NAMESPACE_SEPARATOR ) const
inlineoverridevirtual

Return a full, callable string representation of this.

Parameters
name_keywordsWhether to prefix arguments with "{name}=", where name is the arg name in the header (as with keyword arguments in Python).
namespace_separatorThe string to use to delimit namespaces, i.e. :: in C++ and . in Python.
Returns
A callable string representation of this, which should return an an identical object to this.
Note
The representation with name_keywords=false must be callable in C++. The representation with name_keywords=true should be callable in Python, if there are any bindings.

Implements lsst::gauss2d::Object.

Definition at line 1377 of file model.h.

1378 {
1379 std::string str = type_name_str<Model>(false, namespace_separator) + "("
1380 + (name_keywords ? "data=" : "") + _data->repr(name_keywords, namespace_separator)
1381 + ", " + (name_keywords ? "psfmodels=[" : "[");
1382 for (const auto& x : _psfmodels) str += x->repr(name_keywords, namespace_separator) + ",";
1383 str += (name_keywords ? "], sources=[" : "], [");
1384 for (const auto& s : _sources) str += s->repr(name_keywords, namespace_separator) + ",";
1385 str += (name_keywords ? "], priors=[" : "], [");
1386 return str + "])";
1387 }
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.
Definition model.h:1377

◆ set_extra_param_factors()

template<typename T , typename Image , typename Indices , typename Mask >
void lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::set_extra_param_factors ( const Channel & channel,
ExtraParamFactors & factors,
size_t index ) const
inlineoverridevirtual

Set extra Parameter gradient factors in an existing map.

Parameters
channelThe Channel to set factors for.
factorsThe ExtraParamFactors to set factors for.
indexThe index to begin setting factors at.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 1166 of file model.h.

1167 {
1168 for (auto& source : _sources) {
1169 source->set_extra_param_factors(channel, factors, index);
1170 index += source->get_n_gaussians(channel);
1171 }
1172 }

◆ set_grad_param_factors()

template<typename T , typename Image , typename Indices , typename Mask >
void lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::set_grad_param_factors ( const Channel & channel,
GradParamFactors & factors,
size_t index ) const
inlineoverridevirtual

Set Parameter gradient factors in an existing map.

Parameters
channelThe Channel to set factors for.
factorsThe GradParamFactors to set factors for.
indexThe index to begin setting factors at.

Implements lsst::gauss2d::fit::ParametricModel.

Definition at line 1174 of file model.h.

1175 {
1176 for (auto& source : _sources) {
1177 source->set_grad_param_factors(channel, factors, index);
1178 index += source->get_n_gaussians(channel);
1179 }
1180 }

◆ setup_evaluators()

template<typename T , typename Image , typename Indices , typename Mask >
void lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::setup_evaluators ( EvaluatorMode mode = EvaluatorMode::image,
std::vector< std::vector< std::shared_ptr< Image > > > outputs = {},
std::vector< std::shared_ptr< Image > > residuals = {},
std::vector< std::shared_ptr< Image > > outputs_prior = {},
std::shared_ptr< Image > residuals_prior = nullptr,
bool force = false,
bool print = false )
inline

Setup Evaluator instances for every Observation in _data using the given EvaluatorMode.

Parameters
modeThe EvaluatorMode to use for all Evaluator instances.
outputsA vector of vectors of Image outputs for each Evaluator (created if empty and needed).
residualsA vector of residual Images for each Evaluator (created if empty and needed).
outputs_priorA vector of prior output (Jacobian) Images for each Evaluator (created if empty and needed).
residuals_priorA vector of prior residual Images for each Evaluator (created if empty and needed).
forceWhether to force setting up even if already set up in the same mode
printWhether to print diagnostic statements to stdout.
Note
Different modes require different sized vectors for outputs EvaluatorMode::jacobian requires one Image per free parameter per Observation. EvaluatorMode::loglike_grad requires only one Image(n_rows=1, n_cols=n_params_free)

Definition at line 1199 of file model.h.

1200 {},
1201 std::vector<std::shared_ptr<Image>> residuals = {},
1202 std::vector<std::shared_ptr<Image>> outputs_prior = {},
1203 std::shared_ptr<Image> residuals_prior = nullptr, bool force = false,
1204 bool print = false) {
1205 const size_t n_outputs = outputs.size();
1206 const bool has_outputs = n_outputs > 0;
1207 if (has_outputs) {
1208 if (n_outputs != size()) {
1209 throw std::invalid_argument("outputs.size()=" + std::to_string(n_outputs)
1210 + "!=this->size()=" + std::to_string(size()));
1211 }
1212 }
1213 const size_t n_residuals = residuals.size();
1214 const bool has_residuals = n_residuals > 0;
1215 if (has_residuals) {
1216 if (n_residuals != size()) {
1217 throw std::invalid_argument("residuals.size()=" + std::to_string(n_residuals)
1218 + "!=this->size()=" + std::to_string(size()));
1219 }
1220 }
1221 if (force || (!_is_setup || (mode != _mode))) {
1222 _evaluators.clear();
1223 _grads.clear();
1224 _outputs.clear();
1225 _outputs_prior.clear();
1226 _residuals_prior = nullptr;
1227 _evaluators.reserve(size());
1228 _is_setup = false;
1229
1230 const bool is_jacobian = mode == EvaluatorMode::jacobian;
1231 const bool is_loglike_grad = mode == EvaluatorMode::loglike_grad;
1232
1233 std::vector<ParamBaseCRef> params_free = {};
1234 if (is_jacobian || is_loglike_grad) {
1235 auto filter_free = g2f::ParamFilter{false, true, true, true};
1236 this->get_parameters_const(params_free, &filter_free);
1237 params_free = nonconsecutive_unique<ParamBaseCRef>(params_free);
1238 _n_params_free = params_free.size();
1239 if (print) {
1240 std::cout << "n_params_free=" << _n_params_free << " from params_free:" << std::endl;
1241 std::cout << str_iter_ref<true>(params_free) << std::endl;
1242 }
1243
1244 _grads.reserve(size());
1245 _offsets_params.clear();
1246 }
1247
1248 size_t n_residuals_prior = 0;
1249 bool do_residuals_prior = (_size_priors > 0)
1250 && (is_loglike_grad || is_jacobian || (mode == EvaluatorMode::loglike)
1251 || (_mode == EvaluatorMode::loglike_image));
1252 if (do_residuals_prior) {
1253 for (const auto& prior : this->_priors) {
1254 n_residuals_prior += prior->size();
1255 }
1256
1257 _residuals_prior
1258 = residuals_prior == nullptr
1259 ? (_residuals_prior = std::make_shared<Image>(1, n_residuals_prior))
1260 : std::move(residuals_prior);
1261 if ((_residuals_prior->get_n_rows() != 1)
1262 || (_residuals_prior->get_n_cols() != n_residuals_prior)) {
1263 throw std::invalid_argument("residuals_prior=" + _residuals_prior->str()
1264 + " rows,cols != 1," + std::to_string(n_residuals_prior));
1265 }
1266 if (print) std::cout << "residuals_prior built/validated" << std::endl;
1267 for (size_t col = 0; col < n_residuals_prior; ++col) {
1268 _residuals_prior->set_value(0, col, 0);
1269 }
1270 if (print) std::cout << "residuals_prior reset" << std::endl;
1271
1272 if (is_jacobian) {
1273 if (print) {
1274 std::cout << "setup_evaluators jacobian called for model:" << std::endl;
1275 std::cout << this->str() << std::endl;
1276 }
1277
1278 const size_t n_params_jac = _n_params_free + 1;
1279 if (outputs_prior.size() == 0) {
1280 for (size_t idx_jac = 0; idx_jac < n_params_jac; ++idx_jac) {
1281 _outputs_prior.emplace_back(std::make_shared<Image>(1, n_residuals_prior));
1282 }
1283 if (print) std::cout << "outputs_prior built" << std::endl;
1284 } else if (outputs_prior.size() != n_params_jac) {
1286 "jacobian outputs_prior->size()=" + std::to_string(outputs_prior.size())
1287 + " != (n_params_free + 1 = " + std::to_string(n_params_jac) + ")");
1288 }
1289 _outputs_prior.reserve(n_params_jac);
1290 size_t idx_jac = 0;
1291 for (auto& output_prior : outputs_prior) {
1292 if (output_prior == nullptr) {
1293 throw std::invalid_argument("output_prior[" + std::to_string(idx_jac)
1294 + "] is null");
1295 }
1296 if ((output_prior->get_n_rows() != 1)
1297 || (output_prior->get_n_cols() != n_residuals_prior)) {
1298 throw std::invalid_argument("outputs_prior[" + std::to_string(idx_jac)
1299 + "]=" + output_prior->str() + " rows,cols != 1,"
1300 + std::to_string(n_residuals_prior));
1301 }
1302 for (size_t col = 0; col < n_residuals_prior; ++col) {
1303 output_prior->set_value(0, col, 0);
1304 }
1305 _outputs_prior.emplace_back(std::move(output_prior));
1306 idx_jac++;
1307 }
1308 if (print) std::cout << "outputs_prior validated" << std::endl;
1309 }
1310 }
1311 if (!is_jacobian) {
1312 _outputs.reserve(size());
1313 }
1314
1315 const auto& channels = _data->get_channels();
1316
1317 if (print) std::cout << "making evaluators" << std::endl;
1318
1319 for (size_t idx = 0; idx < _size; ++idx) {
1320 auto result = this->_make_evaluator(idx, mode, outputs,
1321 has_residuals ? residuals[idx] : nullptr, print);
1322 _evaluators.emplace_back(std::move(result.first));
1323 if (is_jacobian || is_loglike_grad) {
1324 _grads.emplace_back(std::move(result.second.second));
1325 } else {
1326 _outputs.emplace_back(std::move(result.second.first));
1327 }
1328 }
1329
1330 if (print) std::cout << "evaluators made" << std::endl;
1331
1332 std::set<ParamBaseCRef> params_prior;
1333 size_t idx_prior = 0;
1334
1335 // Check that the priors are reasonable.
1336 for (const auto& prior : _priors) {
1337 auto eval_prior = prior->evaluate(is_jacobian);
1338 size_t n_resid = prior->size();
1339 size_t n_resid_eval = eval_prior.residuals.size();
1340 if (n_resid != eval_prior.residuals.size()) {
1341 throw std::logic_error(prior->str() + ".size()=" + std::to_string(n_resid)
1342 + " != evaluate(true).residuals.size()="
1343 + std::to_string(n_resid_eval));
1344 }
1345 if (is_jacobian) {
1346 size_t idx_param = 0;
1347 for (const auto& param_jacob : eval_prior.jacobians) {
1348 const auto& param = param_jacob.first;
1349 if (params_prior.find(param) != params_prior.end()) {
1351 "_priors[" + std::to_string(idx_prior) + "]=" + prior->str()
1352 + ".evaluate(true)[" + std::to_string(idx_param)
1353 + "]=" + param.get().str() + " already in prior param set."
1354 + " Did you apply multiple priors to the same parameter?");
1355 }
1356 idx_param++;
1357 }
1358 }
1359 idx_prior++;
1360 }
1361 if (print && (_priors.size() > 0)) std::cout << "priors validated" << std::endl;
1362 if (_size_priors > 0) {
1363 if ((_residuals_prior != nullptr) && (_residuals_prior->get_n_cols() != n_residuals_prior)) {
1364 throw std::invalid_argument("residuals_prior=" + _residuals_prior->str() + " n_residuals="
1365 + std::to_string(n_residuals_prior) + "!= get_n_cols()="
1366 + std::to_string(_residuals_prior->get_n_cols()));
1367 }
1368 }
1369 _is_setup = true;
1370 _mode = mode;
1371 }
1372 }
std::string str() const override
Return a brief, human-readable string representation of this.
Definition model.h:1389
T clear(T... args)
STL namespace.

◆ size()

template<typename T , typename Image , typename Indices , typename Mask >
size_t lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::size ( ) const
inline

Get the size of this->_data.

Definition at line 1375 of file model.h.

1375{ return _size; }

◆ str()

template<typename T , typename Image , typename Indices , typename Mask >
std::string lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::str ( ) const
inlineoverridevirtual

Return a brief, human-readable string representation of this.

Implements lsst::gauss2d::Object.

Definition at line 1389 of file model.h.

1389 {
1390 std::string str = type_name_str<Model>(true) + "(data=" + _data->str() + ", psfmodels=[";
1391 for (const auto& x : _psfmodels) str += x->str() + ",";
1392 str += "], sources=[";
1393 for (const auto& x : _sources) str += x->str() + ",";
1394 str += "], priors=[";
1395 for (const auto& x : _priors) str += x->str() + ",";
1396 return str + "])";
1397 }

◆ verify_jacobian()

template<typename T , typename Image , typename Indices , typename Mask >
std::vector< std::string > lsst::gauss2d::fit::Model< T, Image, Indices, Mask >::verify_jacobian ( double findiff_frac = 1e-5,
double findiff_add = 1e-5,
double rtol = 1e-3,
double atol = 1e-3,
double max_ll_diff = 0 )
inline

Verify that the Jacobian is correct by comparing to finite differences.

Parameters
findiff_fracThe value of the finite difference increment, as a fraction of the parameter value.
findiff_addThe minimum value of the finite difference increment.
rtolThe allowed relative tolerance in the Jacobian as compared to the finite difference.
atolThe allowed absolute tolerance in the Jacobian as compared to the finite difference.
max_ll_diffThe maximum allowed difference between equivalent log-likelihood evaluations. Must be >= 0 and should only be a few orders of magnitude larger than machine epsilon.
Returns
A vector of error messages, one for each Parameter that failed verification.
Note
Verification is done using isclose(), which is modelled after Python's numpy.isclose.

Definition at line 1412 of file model.h.

1413 {
1414 if (!(max_ll_diff >= 0)) {
1415 throw std::invalid_argument("max_ll_diff=" + to_string_float(max_ll_diff) + " !> 0");
1416 }
1417 if (_mode != EvaluatorMode::jacobian) {
1419 }
1420
1421 const size_t n_obs = this->size();
1422 ParamFilter filter{false, true};
1424
1425 for (size_t idx = 0; idx < n_obs; ++idx) {
1426 const auto& grads = *(_grads.at(idx));
1427 const auto& observation = _data->at(idx).get();
1428 const auto& sigma_inv = observation.get_sigma_inverse();
1429
1430 const Channel& channel = observation.get_channel();
1431 const size_t n_cols = observation.get_n_cols();
1432 const size_t n_rows = observation.get_n_rows();
1433
1434 filter.channel = channel;
1435
1436 ParamRefs params{};
1437 this->get_parameters_observation(params, idx, &filter);
1438 params = nonconsecutive_unique(params);
1439
1440 size_t idx_param_max = 0;
1441 for (const auto& param : params) {
1442 auto found = _offsets_params.find(param);
1443 if (found == _offsets_params.end()) {
1444 throw std::runtime_error(
1445 param.get().str()
1446 + " not found in offsets; was it freed after setup_evaluators was called?");
1447 }
1448 size_t idx_param = found->second;
1449 if (idx_param > idx_param_max) idx_param_max = idx_param;
1450 }
1451
1452 if (!(idx_param_max < grads.size())) {
1453 throw std::runtime_error("idx_param_max=" + std::to_string(idx_param_max)
1454 + " !< grads.size()=" + std::to_string(grads.size())
1455 + "; is the jacobian array large enough?");
1456 }
1457
1458 double loglike_jac = this->_evaluate_observation(idx);
1459 const auto& grad = grads[0];
1460 size_t n_nonzero = 0;
1461 for (unsigned int i = 0; i < n_cols; ++i) {
1462 for (unsigned int j = 0; j < n_rows; ++j) {
1463 n_nonzero += grad.get_value(j, i) != 0;
1464 }
1465 }
1466 if (n_nonzero > 0) {
1467 errors.push_back("n_nonzero grads[0] entries=" + std::to_string(n_nonzero));
1468 }
1469
1470 auto result = _make_evaluator(idx, EvaluatorMode::loglike_image);
1471 double loglike_img = result.first->loglike_pixel();
1472 auto is_close_ll = isclose(loglike_img, loglike_jac, 0., max_ll_diff);
1473 if (!is_close_ll.isclose) {
1474 errors.push_back("loglike_jac=" + to_string_float(loglike_jac) + ", loglike_img="
1475 + to_string_float(loglike_img) + " !close (isclose=" + is_close_ll.str()
1476 + ") for obs[" + std::to_string(idx) + "]:");
1477 }
1478
1479 const auto& image_current = *(result.second.first);
1480
1481 for (auto& param_ref : params) {
1482 auto& param = param_ref.get();
1483 const size_t idx_param = _offsets_params.find(param_ref)->second;
1484 const auto& grad = grads[idx_param];
1485
1486 const double value_init = param.get_value();
1487 const double value_transformed = param.get_value_transformed();
1488 double diff = value_transformed * findiff_frac;
1489 if (std::abs(diff) < findiff_add) diff = findiff_add;
1490 diff = finite_difference_param(param, diff);
1491
1492 auto result_diff = _make_evaluator(idx, EvaluatorMode::image);
1493
1494 result_diff.first->loglike_pixel();
1495
1496 size_t n_failed = 0;
1497 std::vector<double> ratios = {};
1498 for (unsigned int i = 0; i < n_cols; ++i) {
1499 for (unsigned int j = 0; j < n_rows; ++j) {
1500 double delta = sigma_inv.get_value(j, i) / diff
1501 * (result_diff.second.first->get_value(j, i)
1502 - image_current.get_value(j, i));
1503 double jac = grad.get_value(j, i);
1504
1505 auto close = isclose(jac, delta, rtol, atol);
1506 if (!close.isclose) {
1507 n_failed++;
1508 ratios.push_back(jac / delta);
1509 }
1510 }
1511 }
1512
1513 param.set_value(value_init);
1514 const double value_new = param.get_value();
1515 if (value_new != value_init) {
1516 throw std::logic_error("Could not return param=" + param.str()
1517 + " to value_init=" + to_string_float(value_init) + "; diff="
1518 + to_string_float(value_new - value_init) + "); check limits");
1519 }
1520 if (n_failed > 0) {
1521 std::sort(ratios.begin(), ratios.end());
1522 double median = ratios[ratios.size() / 2];
1523 errors.push_back("Param[" + std::to_string(idx_param) + "]=" + param.str()
1524 + " failed for obs[" + std::to_string(idx)
1525 + "]: n_failed=" + std::to_string(n_failed)
1526 + "; median evaluated/expected=" + std::to_string(median));
1527 }
1528 }
1529 }
1530 filter.channel = std::nullopt;
1531
1532 ParamRefs params{};
1533 this->get_parameters_observation(params, 0, &filter);
1534 params = nonconsecutive_unique(params);
1535
1536 for (const auto& prior : this->_priors) {
1537 auto result_base = prior->evaluate(true);
1538 auto residuals_base = result_base.residuals;
1539 const size_t n_values = residuals_base.size();
1540
1541 for (const auto& [param_cref, values_base] : result_base.jacobians) {
1542 if (values_base.size() != n_values) {
1543 throw std::logic_error("values_base.size()=" + std::to_string(values_base.size())
1544 + " != residuals_base.size()=" + std::to_string(n_values));
1545 }
1546
1547 // TODO: Determine if it's possible to get mutable ref from set.find
1548 // (set.find only ever seems to return cref, so maybe not)
1549 auto found = std::find(params.begin(), params.end(), param_cref);
1550 if (found == params.end()) {
1551 throw std::logic_error("Couldn't find " + param_cref.get().str() + " in all params");
1552 }
1553 auto& param = (*found).get();
1554
1555 const double value_init = param.get_value();
1556 const double value_transformed = param.get_value_transformed();
1557 double delta = value_transformed * findiff_frac;
1558 if (std::abs(delta) < findiff_add) delta = findiff_add;
1559 delta = finite_difference_param(param, delta);
1560 auto result_new = prior->evaluate(true);
1561
1562 const size_t idx_param = _offsets_params.at(param);
1563
1564 std::vector<double> ratios = {};
1565 size_t n_failed = 0;
1566 size_t idx_value = 0;
1567 for (const double jac_base : values_base) {
1568 double jac_findiff
1569 = (result_new.residuals[idx_value] - result_base.residuals[idx_value]) / delta;
1570 auto close = isclose(jac_base, jac_findiff, rtol, atol);
1571 if (!close.isclose) {
1572 n_failed++;
1573 prior->evaluate(true);
1574 ratios.push_back(jac_base / jac_findiff);
1575 }
1576 idx_value++;
1577 }
1578 param.set_value(value_init);
1579 const double value_new = param.get_value();
1580 if (value_new != value_init) {
1581 throw std::logic_error("Could not return param=" + param.str()
1582 + " to value_init=" + to_string_float(value_init) + "; diff="
1583 + to_string_float(value_new - value_init) + "); check limits");
1584 }
1585 if (n_failed > 0) {
1586 std::sort(ratios.begin(), ratios.end());
1587 double median = ratios[ratios.size() / 2];
1588 errors.push_back("Param[" + std::to_string(idx_param) + "]=" + param.str()
1589 + " failed for prior=" + prior->str()
1590 + ": n_failed=" + std::to_string(n_failed)
1591 + "; median evaluated/expected=" + std::to_string(median));
1592 }
1593 }
1594 }
1595 return errors;
1596 }
T at(T... args)
T begin(T... args)
ParamRefs & get_parameters_observation(ParamRefs &params, size_t idx, ParamFilter *filter=nullptr) const
Same as get_parameters(), but for a single Observation with index idx in _data.
Definition model.h:1138
@ image
Compute the model images only.
std::vector< T > nonconsecutive_unique(const std::vector< T > &vec)
Definition util.h:92
T sort(T... args)

Member Data Documentation

◆ CC_NAMESPACE_SEPARATOR

constexpr std::string_view lsst::gauss2d::Object::CC_NAMESPACE_SEPARATOR = "::"
staticconstexprinherited

The C++ namespace separator.

Definition at line 45 of file object.h.

◆ NULL_STR_GENERAL

constexpr std::string_view lsst::gauss2d::Object::NULL_STR_GENERAL = "None"
staticconstexprinherited

Definition at line 46 of file object.h.

◆ PY_NAMESPACE_SEPARATOR

constexpr std::string_view lsst::gauss2d::Object::PY_NAMESPACE_SEPARATOR = "."
staticconstexprinherited

Definition at line 47 of file object.h.


The documentation for this class was generated from the following file: