26#include "ndarray/eigen.h"
54 -(2.0 *
bbox.getCenterY()) /
bbox.getHeight()));
62 _toChebyshevRange(makeChebyshevRangeTransform(
lsst::
geom::Box2D(
bbox))),
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,
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);
122 Packer
const& packer, Control
const& ctrl) {
124 ndarray::Array<double, 2, 2> tx = ndarray::allocate(
bbox.getWidth(), packer.nx);
125 for (
int x =
bbox.getBeginX(), p = 0; p <
bbox.getWidth(); ++p, ++
x) {
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);
135 for (
int y =
bbox.getBeginY(), i = 0; i <
bbox.getHeight(); ++i, ++
y) {
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))
232 ndarray::Array<double, 1, 1> packed = ndarray::allocate(packer.size);
240 return std::make_shared<ChebyshevBoundedField>(
bbox, _coefficients);
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_)
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")),
324 "coefficients",
"Chebyshev function coefficients, ordered by y then x", nx * ny)) {}
326 PersistenceHelper(table::Schema
const& s)
330class ChebyshevBoundedFieldFactory :
public table::io::PersistableFactory {
332 explicit ChebyshevBoundedFieldFactory(
std::string const& name)
333 :
afw::table::io::PersistableFactory(name) {}
336 CatalogVector
const& catalogs)
const override {
339 table::BaseRecord
const& record =
catalogs.front().front();
340 PersistenceHelper
const keys(record.getSchema());
345 ndarray::Array<double, 2, 2>
coefficients = ndarray::allocate(ny, nx);
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, \
ndarray::Array< double const, 2, 2 > coefficients
#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.
A control object used when fitting ChebyshevBoundedField to data (see ChebyshevBoundedField::fit)
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.
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.
An object passed to Persistable::write to allow it to persist itself.
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 floating-point coordinate rectangle geometry.
An integer coordinate rectangle.
Reports attempts to exceed implementation-defined length limits for some classes.
double integrateTn(int n)
BoxKey< lsst::geom::Box2I > Box2IKey
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