lsst.gauss2d.fit g199a45376c+5e234f8357
 
Loading...
Searching...
No Matches
parameters.h
1/*
2 * This file is part of gauss2d_fit.
3 *
4 * Developed for the LSST Data Management System.
5 * This product includes software developed by the LSST Project
6 * (https://www.lsst.org).
7 * See the COPYRIGHT file at the top-level directory of this distribution
8 * for details of code ownership.
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 */
23
24#ifndef LSST_GAUSS2D_FIT_PYTHON_PARAMETERS_H
25#define LSST_GAUSS2D_FIT_PYTHON_PARAMETERS_H
26
27#include <memory>
28#include <string>
29
30#include <pybind11/attr.h>
31#include <pybind11/pybind11.h>
32#include <pybind11/stl.h>
33
34#include "lsst/modelfit/parameters.h"
35
36#include "gauss2d/fit/parameters.h"
37#include "gauss2d/fit/transforms.h"
38
39#include "utils.h"
40
41namespace py = pybind11;
42using namespace pybind11::literals;
43
44namespace g2f = lsst::gauss2d::fit;
45namespace parameters = lsst::modelfit::parameters;
46
47template <typename T>
48void declare_limits(py::module &m) {
49 using Class = parameters::Limits<T>;
50 std::string pyclass_name = std::string("Limits") + g2f::suffix_type_str<T>();
51 py::classh<Class>(m, pyclass_name.c_str())
52 .def(py::init<T, T, const std::string>(), "min"_a = -std::numeric_limits<T>::infinity(),
53 "max"_a = std::numeric_limits<T>::infinity(), "name"_a = "")
54 .def("check", &Class::check)
55 .def("clip", &Class::clip)
56 .def_property("min", &Class::get_min, &Class::set_min)
57 .def_property("max", &Class::get_max, &Class::set_max)
58 .def("__repr__", [](const Class &self) { return self.repr(true); })
59 .def("__str__", &Class::str);
60}
61
62/*
63 Class is the CRTP class for CRTP parameters, whereas C is the "derived" class.
64 For classes derived from a CRTP parameter, the two are the same.
65 TODO: Come up with a better naming scheme for this.
66*/
67template <class Class, class C, typename... Args>
68auto declare_parameter_methods(py::classh<C, Args...> c) {
69 return c.def_property_readonly("default", &Class::get_default)
70 .def_property_readonly("desc", &Class::get_desc)
71 .def_property("fixed", &Class::get_fixed, &Class::set_fixed)
72 .def_property("free", &Class::get_free, &Class::set_free)
73 .def_property("label", &Class::get_label, &Class::set_label)
74 // Return a copy of the limits, because the C++ func returns a const Limits &
75 // and calling setters on it in Python could cause segfaults
76 .def_property(
77 "limits",
78 [](const C &self) {
79 return parameters::Limits<double>{self.get_limits().get_min(),
80 self.get_limits().get_max()};
81 },
82 &Class::set_limits)
83 .def_property_readonly("linear", &Class::get_linear)
84 .def_property_readonly("min", &Class::get_min)
85 .def_property_readonly("max", &Class::get_max)
86 .def_property_readonly("name", &Class::get_name)
87 .def_property_readonly("ptr", &Class::ptr)
88 // TODO: Figure out if it's possible to bind these
89 //.def_property_readonly_static("limits_maximal", &Class::limits_maximal)
90 // TODO: Determine if this will also segfault when mutating the transform, since
91 // the C++ returns a const ref (most but not all transforms are immutable)
92 .def_property("transform", &Class::get_transform, &Class::set_transform)
93 .def_property_readonly("transform_derivative", &Class::get_transform_derivative)
94 //.def_property_readonly_static("transform_none", &Class::transform_none)
95 .def_property("value", &Class::get_value, &Class::set_value)
96 .def_property("value_transformed", &Class::get_value_transformed, &Class::set_value_transformed)
97
98 /*
99 Additional methods that might be worth wrapping.
100
101 inline std::shared_ptr<Limits<T>> & _get_limits() { return _limits_ptr; }
102 inline std::shared_ptr<Transform<T>> & _get_transform() { return _transform_ptr; }
103 */
104 // This doesn't work - should it?
105 // .def(py::self == py::self)
106 // .def(py::self != py::self)
107 // .def(hash(py::self))
108 .def("__str__", &Class::str);
109}
110
111template <typename T, class C, class... Bases>
112auto declare_parameter(py::module &m, std::string name, std::string suffix = g2f::suffix_type_str<T>()) {
113 using Base = parameters::ParameterBase<T>;
114 using Class = parameters::Parameter<T, C>;
115 return declare_parameter_methods<Class, C, Base>(
116 py::classh<C, Base, Bases...>(m, (name + "Parameter" + suffix).c_str())
117 .def(py::init<T, std::shared_ptr<const parameters::Limits<T>>,
118 std::shared_ptr<const parameters::Transform<T>>,
119 std::shared_ptr<const parameters::Unit>, bool, std::string>(),
120 "value"_a = Class::_get_default(), "limits"_a = nullptr, "transform"_a = nullptr,
121 "unit"_a = lsst::gauss2d::fit::unit_none, "fixed"_a = false, "label"_a = "")
122 .def("__repr__", [](const C &self) { return self.repr(true, "."); }));
123}
124
125template <typename T, class C, class... Bases>
126auto declare_sizeparameter(py::module &m, std::string name) {
127 return declare_parameter<T, C, Bases...>(m, name).def_property("size", &C::get_size, &C::set_size);
128}
129
130template <typename T, class ClassX, class ClassY>
131auto declare_sizeparameter_base(py::module &m, std::string suffix = g2f::suffix_type_str<T>()) {
132 py::classh<ClassX>(m, ("SizeXParameter" + suffix).c_str());
133 py::classh<ClassY>(m, ("SizeYParameter" + suffix).c_str());
134}
135
136template <typename T>
137void declare_transform_base(py::module &m) {
138 using Class = parameters::Transform<T>;
139 py::classh<Class>(m, "TransformD");
140}
141
142template <typename T, class C, bool has_factor, bool has_limits, typename... Arguments>
143void declare_transform_full(py::module &m, std::string name) {
144 using Class = C;
145 auto x = py::classh<Class, parameters::Transform<T>>(
146 m, (name + "TransformD").c_str())
147 .def("description", &Class::description)
148 .def("derivative", &Class::derivative)
149 .def("forward", &Class::forward)
150 .def("reverse", &Class::reverse)
151 .def("__repr__", [](const Class &self) { return self.repr(true, "."); })
152 .def("__str__", &Class::str);
153 if constexpr (has_factor) x.def_property("factor", &Class::get_factor, &Class::set_factor);
154 if constexpr (has_limits) x.def_property("limits", &Class::get_limits, &Class::set_limits);
155 if constexpr (has_factor && has_limits)
156 x.def(py::init<Arguments...>(), "limits"_a = nullptr, "factor"_a = 1.);
157 else if constexpr (has_factor)
158 x.def(py::init<Arguments...>(), "factor"_a = 1.);
159 else if constexpr (has_limits)
160 x.def(py::init<Arguments...>(), "limits"_a = nullptr);
161 else
162 x.def(py::init<>());
163}
164
165template <typename T, class C, typename... Arguments>
166void declare_transform(py::module &m, std::string name) {
167 declare_transform_full<T, C, false, false, Arguments...>(m, name);
168}
169
170#endif