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());
194 ndarray::Array<double const, 1>
const& x,
195 ndarray::Array<double const, 1>
const& y,
202 Packer
const packer(ctrl);
204 return makeMatrix(x, y, result->_toChebyshevRange, packer, ctrl);
215 Packer
const packer(ctrl);
216 ndarray::Array<double, 2, 2> matrix = makeMatrix(bbox, result->_toChebyshevRange, packer, ctrl);
218 ndarray::Array<double, 2, 2> imgCopy = ndarray::allocate(img.
getArray().getShape());
220 ndarray::Array<double const, 1, 1> z = ndarray::flatten<1>(imgCopy);
224 result->_coefficients = packer.unpack(lstsq.
getSolution());
233 (boost::format(
"New x order (%d) exceeds old x order (%d)") % ctrl.
orderX %
234 (_coefficients.getSize<1>() - 1))
239 (boost::format(
"New y order (%d) exceeds old y order (%d)") % ctrl.
orderY %
240 (_coefficients.getSize<0>() - 1))
243 ndarray::Array<double, 2, 2> coefficients = ndarray::allocate(ctrl.
orderY + 1, ctrl.
orderX + 1);
244 coefficients.deep() = _coefficients[ndarray::view(0, ctrl.
orderY + 1)(0, ctrl.
orderX + 1)];
247 ndarray::Array<double, 1, 1> packed = ndarray::allocate(packer.size);
248 packer.pack(packed, coefficients);
249 packer.unpack(coefficients, packed);
266template <
typename CoeffGetter>
267double evaluateFunction1d(CoeffGetter g,
double x,
int size) {
268 double b_kp2 = 0.0, b_kp1 = 0.0;
269 for (
int k = (size - 1); k > 0; --k) {
270 double b_k = g[k] + 2 * x * b_kp1 - b_kp2;
274 return g[0] + x * b_kp1 - b_kp2;
282struct RecursionArrayImitator {
283 double operator[](
int i)
const {
284 return evaluateFunction1d(coefficients[i], x, coefficients.getSize<1>());
287 RecursionArrayImitator(ndarray::Array<double const, 2, 2>
const& coefficients_,
double x_)
288 : coefficients(coefficients_), x(x_) {}
290 ndarray::Array<double const, 2, 2> coefficients;
298 return evaluateFunction1d(RecursionArrayImitator(_coefficients, p.getX()), p.getY(),
299 _coefficients.getSize<0>());
308 return 2.0 / (1.0 - double(n * n));
314 for (ndarray::Size j = 0; j < _coefficients.getSize<0>(); j++) {
315 for (ndarray::Size i = 0; i < _coefficients.getSize<1>(); i++) {
319 return result * determinant;
328struct PersistenceHelper {
334 PersistenceHelper(
int nx,
int ny)
336 orderX(schema.addField<int>(
"order_x",
"maximum Chebyshev function order in x")),
337 bbox(
table::Box2IKey::addFields(schema,
"bbox",
"bounding box",
"pixel")),
338 coefficients(schema.addField<
table::Array<double> >(
339 "coefficients",
"Chebyshev function coefficients, ordered by y then x", nx * ny)) {}
342 : schema(s), orderX(s[
"order_x"]), bbox(s[
"bbox"]), coefficients(s[
"coefficients"]) {}
345class ChebyshevBoundedFieldFactory :
public table::io::PersistableFactory {
347 explicit ChebyshevBoundedFieldFactory(
std::string const& name)
348 : afw::table::io::PersistableFactory(name) {}
350 std::shared_ptr<table::io::Persistable>
read(InputArchive
const& archive,
351 CatalogVector
const& catalogs)
const override {
354 table::BaseRecord
const& record =
catalogs.front().front();
355 PersistenceHelper
const keys(record.getSchema());
356 lsst::geom::Box2I bbox(record.get(
keys.bbox));
360 ndarray::Array<double, 2, 2>
coefficients = ndarray::allocate(ny, nx);
361 ndarray::flatten<1>(coefficients) = record.get(
keys.coefficients);
366std::string getChebyshevBoundedFieldPersistenceName() {
return "ChebyshevBoundedField"; }
368ChebyshevBoundedFieldFactory registration(getChebyshevBoundedFieldPersistenceName());
373 return getChebyshevBoundedFieldPersistenceName();
379 PersistenceHelper
const keys(_coefficients.getSize<1>(), _coefficients.getSize<0>());
382 record->set(keys.orderX, _coefficients.getSize<1>() - 1);
383 record->set(keys.bbox,
getBBox());
384 (*record)[keys.coefficients].deep() = ndarray::flatten<1>(_coefficients);
396 if (!rhsCasted)
return false;
398 return (
getBBox() == rhsCasted->getBBox()) &&
399 (_coefficients.getShape() == rhsCasted->_coefficients.getShape()) &&
400 all(equal(_coefficients, rhsCasted->_coefficients));
405 os <<
"ChebyshevBoundedField (" << _coefficients.getShape() <<
" coefficients in y,x)";
413#define INSTANTIATE(T) \
414 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.
static ndarray::Array< double, 2, 2 > makeFitMatrix(lsst::geom::Box2I const &bbox, ndarray::Array< double const, 1 > const &x, ndarray::Array< double const, 1 > const &y, Control const &ctrl)
Construct a matrix that can be used to fit or evaluate a Chebyshev polynomial at a set of points.
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