26 #include "ndarray/eigen.h" 63 _toChebyshevRange(makeChebyshevRangeTransform(
lsst::
geom::
Box2D(bbox))),
64 _coefficients(coefficients) {}
81 void evaluateBasis1d(ndarray::Array<double, 1, 1>
const& t,
double x) {
82 int const n = t.getSize<0>();
89 for (
int i = 2; i < n; ++i) {
90 t[i] = 2.0 * x * t[i - 1] - t[i - 2];
98 ndarray::Array<double, 2, 2> makeMatrix(ndarray::Array<double const, 1>
const& x,
99 ndarray::Array<double const, 1>
const&
y,
101 Packer
const& packer, Control
const& ctrl) {
102 int const nPoints = x.getSize<0>();
103 ndarray::Array<double, 1, 1> tx = ndarray::allocate(packer.nx);
104 ndarray::Array<double, 1, 1> ty = ndarray::allocate(packer.ny);
105 ndarray::Array<double, 2, 2> out = ndarray::allocate(nPoints, packer.size);
108 for (
int p = 0; p < nPoints; ++p) {
110 evaluateBasis1d(tx, sxy.getX());
111 evaluateBasis1d(ty, sxy.getY());
112 packer.pack(out[p], tx, ty);
123 Packer
const& packer, Control
const& ctrl) {
125 ndarray::Array<double, 2, 2> tx = ndarray::allocate(bbox.
getWidth(), packer.nx);
133 ndarray::Array<double, 2, 2> out = ndarray::allocate(bbox.
getArea(), packer.size);
135 ndarray::Array<double, 1, 1> ty = ndarray::allocate(ctrl.
orderY + 1);
139 for (
int j = 0; j < bbox.
getWidth(); ++j, ++outIter) {
141 packer.pack(*outIter, tx[j], ty);
150 ndarray::Array<double const, 1>
const&
x,
151 ndarray::Array<double const, 1>
const&
y,
152 ndarray::Array<double const, 1>
const&
z,
158 Packer
const packer(ctrl);
160 ndarray::Array<double, 2, 2> matrix = makeMatrix(x, y, result->_toChebyshevRange, packer, ctrl);
164 result->_coefficients = packer.unpack(lstsq.
getSolution());
169 ndarray::Array<double const, 1>
const&
x,
170 ndarray::Array<double const, 1>
const&
y,
171 ndarray::Array<double const, 1>
const&
z,
172 ndarray::Array<double const, 1>
const&
w,
178 Packer
const packer(ctrl);
180 ndarray::Array<double, 2, 2> matrix = makeMatrix(x, y, result->_toChebyshevRange, packer, ctrl);
183 ndarray::asEigenArray(matrix).colwise() *= ndarray::asEigenArray(w);
184 ndarray::Array<double, 1, 1> wz = ndarray::copy(z);
185 ndarray::asEigenArray(wz) *= ndarray::asEigenArray(w);
189 result->_coefficients = packer.unpack(lstsq.
getSolution());
193 template <
typename T>
201 Packer
const packer(ctrl);
202 ndarray::Array<double, 2, 2> matrix = makeMatrix(bbox, result->_toChebyshevRange, packer, ctrl);
204 ndarray::Array<double, 2, 2> imgCopy = ndarray::allocate(img.
getArray().getShape());
206 ndarray::Array<double const, 1, 1>
z = ndarray::flatten<1>(imgCopy);
210 result->_coefficients = packer.unpack(lstsq.getSolution());
217 if (static_cast<std::size_t>(ctrl.
orderX) >= _coefficients.getSize<1>()) {
220 (_coefficients.getSize<1>() - 1))
223 if (static_cast<std::size_t>(ctrl.
orderY) >= _coefficients.getSize<0>()) {
226 (_coefficients.getSize<0>() - 1))
230 coefficients.deep() = _coefficients[ndarray::view(0, ctrl.
orderY + 1)(0, ctrl.
orderX + 1)];
233 ndarray::Array<double, 1, 1> packed = ndarray::allocate(packer.size);
234 packer.pack(packed, coefficients);
235 packer.unpack(coefficients, packed);
241 return std::make_shared<ChebyshevBoundedField>(
bbox, _coefficients);
252 template <
typename CoeffGetter>
253 double evaluateFunction1d(CoeffGetter g,
double x,
int size) {
254 double b_kp2 = 0.0, b_kp1 = 0.0;
255 for (
int k = (size - 1); k > 0; --k) {
256 double b_k = g[k] + 2 * x * b_kp1 - b_kp2;
260 return g[0] + x * b_kp1 - b_kp2;
268 struct RecursionArrayImitator {
269 double operator[](
int i)
const {
273 RecursionArrayImitator(ndarray::Array<double const, 2, 2>
const& coefficients_,
double x_)
284 return evaluateFunction1d(RecursionArrayImitator(_coefficients, p.getX()), p.getY(),
285 _coefficients.getSize<0>());
294 return 2.0 / (1.0 - double(n * n));
300 for (ndarray::Size j = 0; j < _coefficients.getSize<0>(); j++) {
301 for (ndarray::Size i = 0; i < _coefficients.getSize<1>(); i++) {
305 return result * determinant;
314 struct PersistenceHelper {
320 PersistenceHelper(
int nx,
int ny)
322 orderX(schema.addField<int>(
"order_x",
"maximum Chebyshev function order in x")),
323 bbox(table::
Box2IKey::addFields(schema,
"bbox",
"bounding box",
"pixel")),
324 coefficients(schema.addField<table::Array<double> >(
325 "coefficients",
"Chebyshev function coefficients, ordered by y then x", nx * ny)) {}
327 PersistenceHelper(table::Schema
const& s)
331 class ChebyshevBoundedFieldFactory :
public table::io::PersistableFactory {
334 : afw::table::io::PersistableFactory(name) {}
337 CatalogVector
const& catalogs)
const override {
340 table::BaseRecord
const& record = catalogs.front().front();
341 PersistenceHelper
const keys(record.getSchema());
343 int nx = record.get(
keys.orderX) + 1;
344 int ny =
keys.coefficients.getSize() / nx;
346 ndarray::Array<double, 2, 2>
coefficients = ndarray::allocate(ny, nx);
352 std::string getChebyshevBoundedFieldPersistenceName() {
return "ChebyshevBoundedField"; }
354 ChebyshevBoundedFieldFactory registration(getChebyshevBoundedFieldPersistenceName());
359 return getChebyshevBoundedFieldPersistenceName();
365 PersistenceHelper
const keys(_coefficients.getSize<1>(), _coefficients.getSize<0>());
368 record->set(keys.orderX, _coefficients.getSize<1>() - 1);
369 record->set(keys.bbox,
getBBox());
370 (*record)[keys.coefficients].deep() = ndarray::flatten<1>(_coefficients);
382 if (!rhsCasted)
return false;
384 return (
getBBox() == rhsCasted->getBBox()) &&
385 (_coefficients.getShape() == rhsCasted->_coefficients.getShape()) &&
386 all(equal(_coefficients, rhsCasted->_coefficients));
389 std::string ChebyshevBoundedField::toString()
const {
391 os <<
"ChebyshevBoundedField (" << _coefficients.getShape() <<
" coefficients in y,x)";
399 #define INSTANTIATE(T) \ 400 template std::shared_ptr<ChebyshevBoundedField> ChebyshevBoundedField::fit(image::Image<T> const& image, \ def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
double getHeight() const noexcept
1-d interval accessors
ChebyshevBoundedField(lsst::geom::Box2I const &bbox, ndarray::Array< double const, 2, 2 > const &coefficients)
Initialize the field from its bounding box an coefficients.
double getWidth() const noexcept
1-d interval accessors
int getHeight() const noexcept
A floating-point coordinate rectangle geometry.
An object passed to Persistable::write to allow it to persist itself.
BoxKey< lsst::geom::Box2I > Box2IKey
double mean() const override
Compute the mean of this function over its bounding-box.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
double integrateTn(int n)
Reports attempts to exceed implementation-defined length limits for some classes. ...
def scale(algorithm, min, max=None, frame=None)
A control object used when fitting ChebyshevBoundedField to data (see ChebyshevBoundedField::fit) ...
Solver for linear least-squares problems.
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
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.
std::shared_ptr< ChebyshevBoundedField > truncate(Control const &ctrl) const
Return a new ChebyshevBoudedField with maximum orders set by the given control object.
double evaluate(lsst::geom::Point2D const &position) const override
Evaluate the field at the given point.
int getBeginY() const noexcept
FastFinder::Iterator Iterator
bool operator==(BoundedField const &rhs) const override
BoundedFields (of the same sublcass) are equal if their bounding boxes and parameters are equal...
int computeSize() const
Return the number of nonzero coefficients in the Chebyshev function defined by this object...
ChebyshevBoundedFieldControl Control
bool all(CoordinateExpr< N > const &expr) noexcept
Return true if all elements are true.
BoundedField(BoundedField const &)=default
Use the normal equations with a symmetric Eigensystem decomposition.
int orderY
"maximum Chebyshev function order in y" ;
double getCenterX() const noexcept
A base class for image defects.
lsst::geom::Box2I getBBox(ImageOrigin origin=PARENT) const
~ChebyshevBoundedField() override
int getBeginX() const noexcept
lsst::geom::Box2I getBBox() const
Return the bounding box that defines the region where the field is valid.
int getWidth() const noexcept
std::shared_ptr< ChebyshevBoundedField > relocate(lsst::geom::Box2I const &bbox) const
Return a new ChebyshevBoundedField with domain set to the given bounding box.
std::string getPythonModule() const override
Return the fully-qualified Python module that should be imported to guarantee that its factory is reg...
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
bool triangular
"if true, only include terms where the sum of the x and y order " "is less than or equal to max(order...
double getCenterY() const noexcept
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
double integrate() const override
Compute the integral of this function over its bounding-box.
ndarray::Array< double const, 2, 2 > getCoefficients() const
Return the coefficient matrix.
A BoundedField based on 2-d Chebyshev polynomials of the first kind.
An abstract base class for 2-d functions defined on an integer bounding boxes.
ndarray::Array< double const, 1, 1 > getSolution()
Return the vector solution to the least squares problem.
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
std::shared_ptr< BoundedField > operator*(double const scale) const override
Return a scaled BoundedField.
An integer coordinate rectangle.
A class to represent a 2-dimensional array of pixels.
A helper class ChebyshevBoundedField, for mapping trapezoidal matrices to 1-d arrays.
#define INSTANTIATE(FROMSYS, TOSYS)
ndarray::Array< double const, 2, 2 > coefficients
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
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.
int orderX
"maximum Chebyshev function order in x" ;