26#include "ndarray/eigen.h"
60 ndarray::Array<double const, 2, 2>
const& coefficients)
62 _toChebyshevRange(makeChebyshevRangeTransform(
lsst::
geom::Box2D(bbox))),
63 _coefficients(coefficients) {}
77using Packer = detail::TrapezoidalPacker;
80void evaluateBasis1d(ndarray::Array<double, 1, 1>
const& t,
double x) {
81 int const n = t.getSize<0>();
88 for (
int i = 2; i < n; ++i) {
89 t[i] = 2.0 * x * t[i - 1] - t[i - 2];
97ndarray::Array<double, 2, 2> makeMatrix(ndarray::Array<double const, 1>
const& x,
98 ndarray::Array<double const, 1>
const& y,
99 lsst::geom::AffineTransform
const& toChebyshevRange,
100 Packer
const& packer, Control
const& ctrl) {
101 int const nPoints =
x.getSize<0>();
102 ndarray::Array<double, 1, 1> tx = ndarray::allocate(packer.nx);
103 ndarray::Array<double, 1, 1> ty = ndarray::allocate(packer.ny);
104 ndarray::Array<double, 2, 2> out = ndarray::allocate(nPoints, packer.size);
107 for (
int p = 0; p < nPoints; ++p) {
109 evaluateBasis1d(tx, sxy.getX());
110 evaluateBasis1d(ty, sxy.getY());
111 packer.pack(out[p], tx, ty);
120ndarray::Array<double, 2, 2> makeMatrix(lsst::geom::Box2I
const& bbox,
121 lsst::geom::AffineTransform
const& toChebyshevRange,
122 Packer
const& packer, Control
const& ctrl) {
124 ndarray::Array<double, 2, 2> tx = ndarray::allocate(bbox.
getWidth(), packer.nx);
132 ndarray::Array<double, 2, 2> out = ndarray::allocate(bbox.
getArea(), packer.size);
133 ndarray::Array<double, 2, 2>::Iterator outIter = out.begin();
134 ndarray::Array<double, 1, 1> ty = ndarray::allocate(ctrl.orderY + 1);
138 for (
int j = 0; j < bbox.
getWidth(); ++j, ++outIter) {
140 packer.pack(*outIter, tx[j], ty);
149 ndarray::Array<double const, 1>
const& x,
150 ndarray::Array<double const, 1>
const& y,
151 ndarray::Array<double const, 1>
const& z,
157 Packer
const packer(ctrl);
159 ndarray::Array<double, 2, 2> matrix = makeMatrix(x, y, result->_toChebyshevRange, packer, ctrl);
163 result->_coefficients = packer.unpack(lstsq.
getSolution());
168 ndarray::Array<double const, 1>
const& x,
169 ndarray::Array<double const, 1>
const& y,
170 ndarray::Array<double const, 1>
const& z,
171 ndarray::Array<double const, 1>
const& w,
177 Packer
const packer(ctrl);
179 ndarray::Array<double, 2, 2> matrix = makeMatrix(x, y, result->_toChebyshevRange, packer, ctrl);
182 ndarray::asEigenArray(matrix).colwise() *= ndarray::asEigenArray(w);
183 ndarray::Array<double, 1, 1> wz = ndarray::copy(z);
184 ndarray::asEigenArray(wz) *= ndarray::asEigenArray(w);
188 result->_coefficients = packer.unpack(lstsq.
getSolution());
200 Packer
const packer(ctrl);
201 ndarray::Array<double, 2, 2> matrix = makeMatrix(bbox, result->_toChebyshevRange, packer, ctrl);
203 ndarray::Array<double, 2, 2> imgCopy = ndarray::allocate(img.
getArray().getShape());
205 ndarray::Array<double const, 1, 1> z = ndarray::flatten<1>(imgCopy);
209 result->_coefficients = packer.unpack(lstsq.
getSolution());
218 (boost::format(
"New x order (%d) exceeds old x order (%d)") % ctrl.
orderX %
219 (_coefficients.getSize<1>() - 1))
224 (boost::format(
"New y order (%d) exceeds old y order (%d)") % ctrl.
orderY %
225 (_coefficients.getSize<0>() - 1))
228 ndarray::Array<double, 2, 2> coefficients = ndarray::allocate(ctrl.
orderY + 1, ctrl.
orderX + 1);
229 coefficients.deep() = _coefficients[ndarray::view(0, ctrl.
orderY + 1)(0, ctrl.
orderX + 1)];
232 ndarray::Array<double, 1, 1> packed = ndarray::allocate(packer.size);
233 packer.pack(packed, coefficients);
234 packer.unpack(coefficients, packed);
251template <
typename CoeffGetter>
252double evaluateFunction1d(CoeffGetter g,
double x,
int size) {
253 double b_kp2 = 0.0, b_kp1 = 0.0;
254 for (
int k = (size - 1); k > 0; --k) {
255 double b_k = g[k] + 2 * x * b_kp1 - b_kp2;
259 return g[0] + x * b_kp1 - b_kp2;
267struct RecursionArrayImitator {
268 double operator[](
int i)
const {
269 return evaluateFunction1d(coefficients[i], x, coefficients.getSize<1>());
272 RecursionArrayImitator(ndarray::Array<double const, 2, 2>
const& coefficients_,
double x_)
273 : coefficients(coefficients_), x(x_) {}
275 ndarray::Array<double const, 2, 2> coefficients;
283 return evaluateFunction1d(RecursionArrayImitator(_coefficients, p.getX()), p.getY(),
284 _coefficients.getSize<0>());
293 return 2.0 / (1.0 - double(n * n));
299 for (ndarray::Size j = 0; j < _coefficients.getSize<0>(); j++) {
300 for (ndarray::Size i = 0; i < _coefficients.getSize<1>(); i++) {
304 return result * determinant;
313struct PersistenceHelper {
319 PersistenceHelper(
int nx,
int ny)
321 orderX(schema.addField<int>(
"order_x",
"maximum Chebyshev function order in x")),
322 bbox(
table::Box2IKey::addFields(schema,
"bbox",
"bounding box",
"pixel")),
323 coefficients(schema.addField<
table::Array<double> >(
324 "coefficients",
"Chebyshev function coefficients, ordered by y then x", nx * ny)) {}
327 : schema(s), orderX(s[
"order_x"]), bbox(s[
"bbox"]), coefficients(s[
"coefficients"]) {}
330class ChebyshevBoundedFieldFactory :
public table::io::PersistableFactory {
332 explicit ChebyshevBoundedFieldFactory(
std::string const& name)
333 : afw::table::io::PersistableFactory(name) {}
335 std::shared_ptr<table::io::Persistable>
read(InputArchive
const& archive,
336 CatalogVector
const& catalogs)
const override {
339 table::BaseRecord
const& record =
catalogs.front().front();
340 PersistenceHelper
const keys(record.getSchema());
341 lsst::geom::Box2I bbox(record.get(
keys.bbox));
345 ndarray::Array<double, 2, 2>
coefficients = ndarray::allocate(ny, nx);
346 ndarray::flatten<1>(coefficients) = record.get(
keys.coefficients);
351std::string getChebyshevBoundedFieldPersistenceName() {
return "ChebyshevBoundedField"; }
353ChebyshevBoundedFieldFactory registration(getChebyshevBoundedFieldPersistenceName());
358 return getChebyshevBoundedFieldPersistenceName();
364 PersistenceHelper
const keys(_coefficients.getSize<1>(), _coefficients.getSize<0>());
367 record->set(keys.orderX, _coefficients.getSize<1>() - 1);
368 record->set(keys.bbox,
getBBox());
369 (*record)[keys.coefficients].deep() = ndarray::flatten<1>(_coefficients);
381 if (!rhsCasted)
return false;
383 return (
getBBox() == rhsCasted->getBBox()) &&
384 (_coefficients.getShape() == rhsCasted->_coefficients.getShape()) &&
385 all(equal(_coefficients, rhsCasted->_coefficients));
390 os <<
"ChebyshevBoundedField (" << _coefficients.getShape() <<
" coefficients in y,x)";
398#define INSTANTIATE(T) \
399 template std::shared_ptr<ChebyshevBoundedField> ChebyshevBoundedField::fit(image::Image<T> const& image, \
#define INSTANTIATE(FROMSYS, TOSYS)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
A class to represent a 2-dimensional array of pixels.
An abstract base class for 2-d functions defined on an integer bounding boxes.
lsst::geom::Box2I getBBox() const
Return the bounding box that defines the region where the field is valid.
BoundedField(BoundedField const &)=default
int computeSize() const
Return the number of nonzero coefficients in the Chebyshev function defined by this object.
bool triangular
"if true, only include terms where the sum of the x and y order " "is less than or equal to max(order...
int orderY
"maximum Chebyshev function order in y" ;
int orderX
"maximum Chebyshev function order in x" ;
A BoundedField based on 2-d Chebyshev polynomials of the first kind.
static std::shared_ptr< ChebyshevBoundedField > fit(lsst::geom::Box2I const &bbox, ndarray::Array< double const, 1 > const &x, ndarray::Array< double const, 1 > const &y, ndarray::Array< double const, 1 > const &z, Control const &ctrl)
Fit a Chebyshev approximation to non-gridded data with equal weights.
std::shared_ptr< ChebyshevBoundedField > relocate(lsst::geom::Box2I const &bbox) const
Return a new ChebyshevBoundedField with domain set to the given bounding box.
bool operator==(BoundedField const &rhs) const override
BoundedFields (of the same sublcass) are equal if their bounding boxes and parameters are equal.
ndarray::Array< double const, 2, 2 > getCoefficients() const
Return the coefficient matrix.
std::shared_ptr< BoundedField > operator*(double const scale) const override
Return a scaled BoundedField.
ChebyshevBoundedFieldControl Control
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
std::string getPythonModule() const override
Return the fully-qualified Python module that should be imported to guarantee that its factory is reg...
double evaluate(lsst::geom::Point2D const &position) const override
Evaluate the field at the given point.
double mean() const override
Compute the mean of this function over its bounding-box.
~ChebyshevBoundedField() override
std::string toString() const override
std::shared_ptr< ChebyshevBoundedField > truncate(Control const &ctrl) const
Return a new ChebyshevBoudedField with maximum orders set by the given control object.
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
ChebyshevBoundedField(lsst::geom::Box2I const &bbox, ndarray::Array< double const, 2, 2 > const &coefficients)
Initialize the field from its bounding box an coefficients.
double integrate() const override
Compute the integral of this function over its bounding-box.
Solver for linear least-squares problems.
ndarray::Array< double const, 1, 1 > getSolution()
Return the vector solution to the least squares problem.
static LeastSquares fromDesignMatrix(ndarray::Array< T1, 2, C1 > const &design, ndarray::Array< T2, 1, C2 > const &data, Factorization factorization=NORMAL_EIGENSYSTEM)
Initialize from the design matrix and data vector given as ndarrays.
@ NORMAL_EIGENSYSTEM
Use the normal equations with a symmetric Eigensystem decomposition.
A class used as a handle to a particular field in a table.
Defines the fields and offsets for a table.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
A CRTP facade class for subclasses of Persistable.
io::OutputArchiveHandle OutputArchiveHandle
A floating-point coordinate rectangle geometry.
double getWidth() const noexcept
1-d interval accessors
double getCenterX() const noexcept
Return true if the box contains no points.
double getHeight() const noexcept
1-d interval accessors
double getCenterY() const noexcept
Return true if the box contains no points.
An integer coordinate rectangle.
int getBeginX() const noexcept
int getHeight() const noexcept
int getWidth() const noexcept
int getBeginY() const noexcept
Reports attempts to exceed implementation-defined length limits for some classes.
double integrateTn(int n)
CatalogT< BaseRecord > BaseCatalog
BoxKey< lsst::geom::Box2I > Box2IKey
Extent< double, 2 > Extent2D
Point< double, 2 > Point2D
decltype(sizeof(void *)) size_t
A helper class ChebyshevBoundedField, for mapping trapezoidal matrices to 1-d arrays.
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override