mirror of
https://asciireactor.com/otho/psdlag-agn.git
synced 2024-11-24 06:35:04 +00:00
30716 lines
1.1 MiB
30716 lines
1.1 MiB
/*************************************************************************
|
|
Copyright (c) Sergey Bochkanov (ALGLIB project).
|
|
|
|
>>> SOURCE LICENSE >>>
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation (www.fsf.org); either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
A copy of the GNU General Public License is available at
|
|
http://www.fsf.org/licensing/licenses
|
|
>>> END OF LICENSE >>>
|
|
*************************************************************************/
|
|
#include "stdafx.h"
|
|
#include "interpolation.h"
|
|
|
|
// disable some irrelevant warnings
|
|
#if (AE_COMPILER==AE_MSVC)
|
|
#pragma warning(disable:4100)
|
|
#pragma warning(disable:4127)
|
|
#pragma warning(disable:4702)
|
|
#pragma warning(disable:4996)
|
|
#endif
|
|
using namespace std;
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// THIS SECTION CONTAINS IMPLEMENTATION OF C++ INTERFACE
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
namespace alglib
|
|
{
|
|
|
|
|
|
/*************************************************************************
|
|
IDW interpolant.
|
|
*************************************************************************/
|
|
_idwinterpolant_owner::_idwinterpolant_owner()
|
|
{
|
|
p_struct = (alglib_impl::idwinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_idwinterpolant_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_idwinterpolant_owner::_idwinterpolant_owner(const _idwinterpolant_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::idwinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_idwinterpolant_init_copy(p_struct, const_cast<alglib_impl::idwinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_idwinterpolant_owner& _idwinterpolant_owner::operator=(const _idwinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_idwinterpolant_clear(p_struct);
|
|
if( !alglib_impl::_idwinterpolant_init_copy(p_struct, const_cast<alglib_impl::idwinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_idwinterpolant_owner::~_idwinterpolant_owner()
|
|
{
|
|
alglib_impl::_idwinterpolant_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::idwinterpolant* _idwinterpolant_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::idwinterpolant* _idwinterpolant_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::idwinterpolant*>(p_struct);
|
|
}
|
|
idwinterpolant::idwinterpolant() : _idwinterpolant_owner()
|
|
{
|
|
}
|
|
|
|
idwinterpolant::idwinterpolant(const idwinterpolant &rhs):_idwinterpolant_owner(rhs)
|
|
{
|
|
}
|
|
|
|
idwinterpolant& idwinterpolant::operator=(const idwinterpolant &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_idwinterpolant_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
idwinterpolant::~idwinterpolant()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
IDW interpolation
|
|
|
|
INPUT PARAMETERS:
|
|
Z - IDW interpolant built with one of model building
|
|
subroutines.
|
|
X - array[0..NX-1], interpolation point
|
|
|
|
Result:
|
|
IDW interpolant Z(X)
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc(const idwinterpolant &z, const real_1d_array &x)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::idwcalc(const_cast<alglib_impl::idwinterpolant*>(z.c_ptr()), const_cast<alglib_impl::ae_vector*>(x.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
IDW interpolant using modified Shepard method for uniform point
|
|
distributions.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - X and Y values, array[0..N-1,0..NX].
|
|
First NX columns contain X-values, last column contain
|
|
Y-values.
|
|
N - number of nodes, N>0.
|
|
NX - space dimension, NX>=1.
|
|
D - nodal function type, either:
|
|
* 0 constant model. Just for demonstration only, worst
|
|
model ever.
|
|
* 1 linear model, least squares fitting. Simpe model for
|
|
datasets too small for quadratic models
|
|
* 2 quadratic model, least squares fitting. Best model
|
|
available (if your dataset is large enough).
|
|
* -1 "fast" linear model, use with caution!!! It is
|
|
significantly faster than linear/quadratic and better
|
|
than constant model. But it is less robust (especially
|
|
in the presence of noise).
|
|
NQ - number of points used to calculate nodal functions (ignored
|
|
for constant models). NQ should be LARGER than:
|
|
* max(1.5*(1+NX),2^NX+1) for linear model,
|
|
* max(3/4*(NX+2)*(NX+1),2^NX+1) for quadratic model.
|
|
Values less than this threshold will be silently increased.
|
|
NW - number of points used to calculate weights and to interpolate.
|
|
Required: >=2^NX+1, values less than this threshold will be
|
|
silently increased.
|
|
Recommended value: about 2*NQ
|
|
|
|
OUTPUT PARAMETERS:
|
|
Z - IDW interpolant.
|
|
|
|
NOTES:
|
|
* best results are obtained with quadratic models, worst - with constant
|
|
models
|
|
* when N is large, NQ and NW must be significantly smaller than N both
|
|
to obtain optimal performance and to obtain optimal accuracy. In 2 or
|
|
3-dimensional tasks NQ=15 and NW=25 are good values to start with.
|
|
* NQ and NW may be greater than N. In such cases they will be
|
|
automatically decreased.
|
|
* this subroutine is always succeeds (as long as correct parameters are
|
|
passed).
|
|
* see 'Multivariate Interpolation of Large Sets of Scattered Data' by
|
|
Robert J. Renka for more information on this algorithm.
|
|
* this subroutine assumes that point distribution is uniform at the small
|
|
scales. If it isn't - for example, points are concentrated along
|
|
"lines", but "lines" distribution is uniform at the larger scale - then
|
|
you should use IDWBuildModifiedShepardR()
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildmodifiedshepard(const real_2d_array &xy, const ae_int_t n, const ae_int_t nx, const ae_int_t d, const ae_int_t nq, const ae_int_t nw, idwinterpolant &z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::idwbuildmodifiedshepard(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, nx, d, nq, nw, const_cast<alglib_impl::idwinterpolant*>(z.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
IDW interpolant using modified Shepard method for non-uniform datasets.
|
|
|
|
This type of model uses constant nodal functions and interpolates using
|
|
all nodes which are closer than user-specified radius R. It may be used
|
|
when points distribution is non-uniform at the small scale, but it is at
|
|
the distances as large as R.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - X and Y values, array[0..N-1,0..NX].
|
|
First NX columns contain X-values, last column contain
|
|
Y-values.
|
|
N - number of nodes, N>0.
|
|
NX - space dimension, NX>=1.
|
|
R - radius, R>0
|
|
|
|
OUTPUT PARAMETERS:
|
|
Z - IDW interpolant.
|
|
|
|
NOTES:
|
|
* if there is less than IDWKMin points within R-ball, algorithm selects
|
|
IDWKMin closest ones, so that continuity properties of interpolant are
|
|
preserved even far from points.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 11.04.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildmodifiedshepardr(const real_2d_array &xy, const ae_int_t n, const ae_int_t nx, const double r, idwinterpolant &z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::idwbuildmodifiedshepardr(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, nx, r, const_cast<alglib_impl::idwinterpolant*>(z.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
IDW model for noisy data.
|
|
|
|
This subroutine may be used to handle noisy data, i.e. data with noise in
|
|
OUTPUT values. It differs from IDWBuildModifiedShepard() in the following
|
|
aspects:
|
|
* nodal functions are not constrained to pass through nodes: Qi(xi)<>yi,
|
|
i.e. we have fitting instead of interpolation.
|
|
* weights which are used during least squares fitting stage are all equal
|
|
to 1.0 (independently of distance)
|
|
* "fast"-linear or constant nodal functions are not supported (either not
|
|
robust enough or too rigid)
|
|
|
|
This problem require far more complex tuning than interpolation problems.
|
|
Below you can find some recommendations regarding this problem:
|
|
* focus on tuning NQ; it controls noise reduction. As for NW, you can just
|
|
make it equal to 2*NQ.
|
|
* you can use cross-validation to determine optimal NQ.
|
|
* optimal NQ is a result of complex tradeoff between noise level (more
|
|
noise = larger NQ required) and underlying function complexity (given
|
|
fixed N, larger NQ means smoothing of compex features in the data). For
|
|
example, NQ=N will reduce noise to the minimum level possible, but you
|
|
will end up with just constant/linear/quadratic (depending on D) least
|
|
squares model for the whole dataset.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - X and Y values, array[0..N-1,0..NX].
|
|
First NX columns contain X-values, last column contain
|
|
Y-values.
|
|
N - number of nodes, N>0.
|
|
NX - space dimension, NX>=1.
|
|
D - nodal function degree, either:
|
|
* 1 linear model, least squares fitting. Simpe model for
|
|
datasets too small for quadratic models (or for very
|
|
noisy problems).
|
|
* 2 quadratic model, least squares fitting. Best model
|
|
available (if your dataset is large enough).
|
|
NQ - number of points used to calculate nodal functions. NQ should
|
|
be significantly larger than 1.5 times the number of
|
|
coefficients in a nodal function to overcome effects of noise:
|
|
* larger than 1.5*(1+NX) for linear model,
|
|
* larger than 3/4*(NX+2)*(NX+1) for quadratic model.
|
|
Values less than this threshold will be silently increased.
|
|
NW - number of points used to calculate weights and to interpolate.
|
|
Required: >=2^NX+1, values less than this threshold will be
|
|
silently increased.
|
|
Recommended value: about 2*NQ or larger
|
|
|
|
OUTPUT PARAMETERS:
|
|
Z - IDW interpolant.
|
|
|
|
NOTES:
|
|
* best results are obtained with quadratic models, linear models are not
|
|
recommended to use unless you are pretty sure that it is what you want
|
|
* this subroutine is always succeeds (as long as correct parameters are
|
|
passed).
|
|
* see 'Multivariate Interpolation of Large Sets of Scattered Data' by
|
|
Robert J. Renka for more information on this algorithm.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildnoisy(const real_2d_array &xy, const ae_int_t n, const ae_int_t nx, const ae_int_t d, const ae_int_t nq, const ae_int_t nw, idwinterpolant &z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::idwbuildnoisy(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, nx, d, nq, nw, const_cast<alglib_impl::idwinterpolant*>(z.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Barycentric interpolant.
|
|
*************************************************************************/
|
|
_barycentricinterpolant_owner::_barycentricinterpolant_owner()
|
|
{
|
|
p_struct = (alglib_impl::barycentricinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_barycentricinterpolant_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_barycentricinterpolant_owner::_barycentricinterpolant_owner(const _barycentricinterpolant_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::barycentricinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_barycentricinterpolant_init_copy(p_struct, const_cast<alglib_impl::barycentricinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_barycentricinterpolant_owner& _barycentricinterpolant_owner::operator=(const _barycentricinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_barycentricinterpolant_clear(p_struct);
|
|
if( !alglib_impl::_barycentricinterpolant_init_copy(p_struct, const_cast<alglib_impl::barycentricinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_barycentricinterpolant_owner::~_barycentricinterpolant_owner()
|
|
{
|
|
alglib_impl::_barycentricinterpolant_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::barycentricinterpolant* _barycentricinterpolant_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::barycentricinterpolant* _barycentricinterpolant_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::barycentricinterpolant*>(p_struct);
|
|
}
|
|
barycentricinterpolant::barycentricinterpolant() : _barycentricinterpolant_owner()
|
|
{
|
|
}
|
|
|
|
barycentricinterpolant::barycentricinterpolant(const barycentricinterpolant &rhs):_barycentricinterpolant_owner(rhs)
|
|
{
|
|
}
|
|
|
|
barycentricinterpolant& barycentricinterpolant::operator=(const barycentricinterpolant &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_barycentricinterpolant_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
barycentricinterpolant::~barycentricinterpolant()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
Rational interpolation using barycentric formula
|
|
|
|
F(t) = SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))
|
|
|
|
Input parameters:
|
|
B - barycentric interpolant built with one of model building
|
|
subroutines.
|
|
T - interpolation point
|
|
|
|
Result:
|
|
barycentric interpolant F(t)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double barycentriccalc(const barycentricinterpolant &b, const double t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::barycentriccalc(const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), t, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Differentiation of barycentric interpolant: first derivative.
|
|
|
|
Algorithm used in this subroutine is very robust and should not fail until
|
|
provided with values too close to MaxRealNumber (usually MaxRealNumber/N
|
|
or greater will overflow).
|
|
|
|
INPUT PARAMETERS:
|
|
B - barycentric interpolant built with one of model building
|
|
subroutines.
|
|
T - interpolation point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - barycentric interpolant at T
|
|
DF - first derivative
|
|
|
|
NOTE
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricdiff1(const barycentricinterpolant &b, const double t, double &f, double &df)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentricdiff1(const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), t, &f, &df, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Differentiation of barycentric interpolant: first/second derivatives.
|
|
|
|
INPUT PARAMETERS:
|
|
B - barycentric interpolant built with one of model building
|
|
subroutines.
|
|
T - interpolation point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - barycentric interpolant at T
|
|
DF - first derivative
|
|
D2F - second derivative
|
|
|
|
NOTE: this algorithm may fail due to overflow/underflor if used on data
|
|
whose values are close to MaxRealNumber or MinRealNumber. Use more robust
|
|
BarycentricDiff1() subroutine in such cases.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricdiff2(const barycentricinterpolant &b, const double t, double &f, double &df, double &d2f)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentricdiff2(const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), t, &f, &df, &d2f, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the argument.
|
|
|
|
INPUT PARAMETERS:
|
|
B - rational interpolant in barycentric form
|
|
CA, CB - transformation coefficients: x = CA*t + CB
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - transformed interpolant with X replaced by T
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentriclintransx(const barycentricinterpolant &b, const double ca, const double cb)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentriclintransx(const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), ca, cb, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the barycentric
|
|
interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
B - rational interpolant in barycentric form
|
|
CA, CB - transformation coefficients: B2(x) = CA*B(x) + CB
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - transformed interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentriclintransy(const barycentricinterpolant &b, const double ca, const double cb)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentriclintransy(const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), ca, cb, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Extracts X/Y/W arrays from rational interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
B - barycentric interpolant
|
|
|
|
OUTPUT PARAMETERS:
|
|
N - nodes count, N>0
|
|
X - interpolation nodes, array[0..N-1]
|
|
F - function values, array[0..N-1]
|
|
W - barycentric weights, array[0..N-1]
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricunpack(const barycentricinterpolant &b, ae_int_t &n, real_1d_array &x, real_1d_array &y, real_1d_array &w)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentricunpack(const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), &n, const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Rational interpolant from X/Y/W arrays
|
|
|
|
F(t) = SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))
|
|
|
|
INPUT PARAMETERS:
|
|
X - interpolation nodes, array[0..N-1]
|
|
F - function values, array[0..N-1]
|
|
W - barycentric weights, array[0..N-1]
|
|
N - nodes count, N>0
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - barycentric interpolant built from (X, Y, W)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricbuildxyw(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const ae_int_t n, barycentricinterpolant &b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentricbuildxyw(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Rational interpolant without poles
|
|
|
|
The subroutine constructs the rational interpolating function without real
|
|
poles (see 'Barycentric rational interpolation with no poles and high
|
|
rates of approximation', Michael S. Floater. and Kai Hormann, for more
|
|
information on this subject).
|
|
|
|
Input parameters:
|
|
X - interpolation nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of nodes, N>0.
|
|
D - order of the interpolation scheme, 0 <= D <= N-1.
|
|
D<0 will cause an error.
|
|
D>=N it will be replaced with D=N-1.
|
|
if you don't know what D to choose, use small value about 3-5.
|
|
|
|
Output parameters:
|
|
B - barycentric interpolant.
|
|
|
|
Note:
|
|
this algorithm always succeeds and calculates the weights with close
|
|
to machine precision.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 17.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricbuildfloaterhormann(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t d, barycentricinterpolant &b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentricbuildfloaterhormann(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, d, const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Conversion from barycentric representation to Chebyshev basis.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
P - polynomial in barycentric form
|
|
A,B - base interval for Chebyshev polynomials (see below)
|
|
A<>B
|
|
|
|
OUTPUT PARAMETERS
|
|
T - coefficients of Chebyshev representation;
|
|
P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N-1 },
|
|
where Ti - I-th Chebyshev polynomial.
|
|
|
|
NOTES:
|
|
barycentric interpolant passed as P may be either polynomial obtained
|
|
from polynomial interpolation/ fitting or rational function which is
|
|
NOT polynomial. We can't distinguish between these two cases, and this
|
|
algorithm just tries to work assuming that P IS a polynomial. If not,
|
|
algorithm will return results, but they won't have any meaning.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbar2cheb(const barycentricinterpolant &p, const double a, const double b, real_1d_array &t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbar2cheb(const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), a, b, const_cast<alglib_impl::ae_vector*>(t.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Conversion from Chebyshev basis to barycentric representation.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
T - coefficients of Chebyshev representation;
|
|
P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N },
|
|
where Ti - I-th Chebyshev polynomial.
|
|
N - number of coefficients:
|
|
* if given, only leading N elements of T are used
|
|
* if not given, automatically determined from size of T
|
|
A,B - base interval for Chebyshev polynomials (see above)
|
|
A<B
|
|
|
|
OUTPUT PARAMETERS
|
|
P - polynomial in barycentric form
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialcheb2bar(const real_1d_array &t, const ae_int_t n, const double a, const double b, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialcheb2bar(const_cast<alglib_impl::ae_vector*>(t.c_ptr()), n, a, b, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Conversion from Chebyshev basis to barycentric representation.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
T - coefficients of Chebyshev representation;
|
|
P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N },
|
|
where Ti - I-th Chebyshev polynomial.
|
|
N - number of coefficients:
|
|
* if given, only leading N elements of T are used
|
|
* if not given, automatically determined from size of T
|
|
A,B - base interval for Chebyshev polynomials (see above)
|
|
A<B
|
|
|
|
OUTPUT PARAMETERS
|
|
P - polynomial in barycentric form
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialcheb2bar(const real_1d_array &t, const double a, const double b, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = t.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialcheb2bar(const_cast<alglib_impl::ae_vector*>(t.c_ptr()), n, a, b, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Conversion from barycentric representation to power basis.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
P - polynomial in barycentric form
|
|
C - offset (see below); 0.0 is used as default value.
|
|
S - scale (see below); 1.0 is used as default value. S<>0.
|
|
|
|
OUTPUT PARAMETERS
|
|
A - coefficients, P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 }
|
|
N - number of coefficients (polynomial degree plus 1)
|
|
|
|
NOTES:
|
|
1. this function accepts offset and scale, which can be set to improve
|
|
numerical properties of polynomial. For example, if P was obtained as
|
|
result of interpolation on [-1,+1], you can set C=0 and S=1 and
|
|
represent P as sum of 1, x, x^2, x^3 and so on. In most cases you it
|
|
is exactly what you need.
|
|
|
|
However, if your interpolation model was built on [999,1001], you will
|
|
see significant growth of numerical errors when using {1, x, x^2, x^3}
|
|
as basis. Representing P as sum of 1, (x-1000), (x-1000)^2, (x-1000)^3
|
|
will be better option. Such representation can be obtained by using
|
|
1000.0 as offset C and 1.0 as scale S.
|
|
|
|
2. power basis is ill-conditioned and tricks described above can't solve
|
|
this problem completely. This function will return coefficients in
|
|
any case, but for N>8 they will become unreliable. However, N's
|
|
less than 5 are pretty safe.
|
|
|
|
3. barycentric interpolant passed as P may be either polynomial obtained
|
|
from polynomial interpolation/ fitting or rational function which is
|
|
NOT polynomial. We can't distinguish between these two cases, and this
|
|
algorithm just tries to work assuming that P IS a polynomial. If not,
|
|
algorithm will return results, but they won't have any meaning.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbar2pow(const barycentricinterpolant &p, const double c, const double s, real_1d_array &a)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbar2pow(const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), c, s, const_cast<alglib_impl::ae_vector*>(a.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Conversion from barycentric representation to power basis.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
P - polynomial in barycentric form
|
|
C - offset (see below); 0.0 is used as default value.
|
|
S - scale (see below); 1.0 is used as default value. S<>0.
|
|
|
|
OUTPUT PARAMETERS
|
|
A - coefficients, P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 }
|
|
N - number of coefficients (polynomial degree plus 1)
|
|
|
|
NOTES:
|
|
1. this function accepts offset and scale, which can be set to improve
|
|
numerical properties of polynomial. For example, if P was obtained as
|
|
result of interpolation on [-1,+1], you can set C=0 and S=1 and
|
|
represent P as sum of 1, x, x^2, x^3 and so on. In most cases you it
|
|
is exactly what you need.
|
|
|
|
However, if your interpolation model was built on [999,1001], you will
|
|
see significant growth of numerical errors when using {1, x, x^2, x^3}
|
|
as basis. Representing P as sum of 1, (x-1000), (x-1000)^2, (x-1000)^3
|
|
will be better option. Such representation can be obtained by using
|
|
1000.0 as offset C and 1.0 as scale S.
|
|
|
|
2. power basis is ill-conditioned and tricks described above can't solve
|
|
this problem completely. This function will return coefficients in
|
|
any case, but for N>8 they will become unreliable. However, N's
|
|
less than 5 are pretty safe.
|
|
|
|
3. barycentric interpolant passed as P may be either polynomial obtained
|
|
from polynomial interpolation/ fitting or rational function which is
|
|
NOT polynomial. We can't distinguish between these two cases, and this
|
|
algorithm just tries to work assuming that P IS a polynomial. If not,
|
|
algorithm will return results, but they won't have any meaning.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbar2pow(const barycentricinterpolant &p, real_1d_array &a)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
double c;
|
|
double s;
|
|
|
|
c = 0;
|
|
s = 1;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbar2pow(const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), c, s, const_cast<alglib_impl::ae_vector*>(a.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Conversion from power basis to barycentric representation.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - coefficients, P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 }
|
|
N - number of coefficients (polynomial degree plus 1)
|
|
* if given, only leading N elements of A are used
|
|
* if not given, automatically determined from size of A
|
|
C - offset (see below); 0.0 is used as default value.
|
|
S - scale (see below); 1.0 is used as default value. S<>0.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - polynomial in barycentric form
|
|
|
|
|
|
NOTES:
|
|
1. this function accepts offset and scale, which can be set to improve
|
|
numerical properties of polynomial. For example, if you interpolate on
|
|
[-1,+1], you can set C=0 and S=1 and convert from sum of 1, x, x^2,
|
|
x^3 and so on. In most cases you it is exactly what you need.
|
|
|
|
However, if your interpolation model was built on [999,1001], you will
|
|
see significant growth of numerical errors when using {1, x, x^2, x^3}
|
|
as input basis. Converting from sum of 1, (x-1000), (x-1000)^2,
|
|
(x-1000)^3 will be better option (you have to specify 1000.0 as offset
|
|
C and 1.0 as scale S).
|
|
|
|
2. power basis is ill-conditioned and tricks described above can't solve
|
|
this problem completely. This function will return barycentric model
|
|
in any case, but for N>8 accuracy well degrade. However, N's less than
|
|
5 are pretty safe.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialpow2bar(const real_1d_array &a, const ae_int_t n, const double c, const double s, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialpow2bar(const_cast<alglib_impl::ae_vector*>(a.c_ptr()), n, c, s, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Conversion from power basis to barycentric representation.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - coefficients, P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 }
|
|
N - number of coefficients (polynomial degree plus 1)
|
|
* if given, only leading N elements of A are used
|
|
* if not given, automatically determined from size of A
|
|
C - offset (see below); 0.0 is used as default value.
|
|
S - scale (see below); 1.0 is used as default value. S<>0.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - polynomial in barycentric form
|
|
|
|
|
|
NOTES:
|
|
1. this function accepts offset and scale, which can be set to improve
|
|
numerical properties of polynomial. For example, if you interpolate on
|
|
[-1,+1], you can set C=0 and S=1 and convert from sum of 1, x, x^2,
|
|
x^3 and so on. In most cases you it is exactly what you need.
|
|
|
|
However, if your interpolation model was built on [999,1001], you will
|
|
see significant growth of numerical errors when using {1, x, x^2, x^3}
|
|
as input basis. Converting from sum of 1, (x-1000), (x-1000)^2,
|
|
(x-1000)^3 will be better option (you have to specify 1000.0 as offset
|
|
C and 1.0 as scale S).
|
|
|
|
2. power basis is ill-conditioned and tricks described above can't solve
|
|
this problem completely. This function will return barycentric model
|
|
in any case, but for N>8 accuracy well degrade. However, N's less than
|
|
5 are pretty safe.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialpow2bar(const real_1d_array &a, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
double c;
|
|
double s;
|
|
|
|
n = a.length();
|
|
c = 0;
|
|
s = 1;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialpow2bar(const_cast<alglib_impl::ae_vector*>(a.c_ptr()), n, c, s, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant: generation of the model on the general grid.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
X - abscissas, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - number of points, N>=1
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuild(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuild(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant: generation of the model on the general grid.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
X - abscissas, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - number of points, N>=1
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuild(const real_1d_array &x, const real_1d_array &y, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'polynomialbuild': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuild(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant: generation of the model on equidistant grid.
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1]
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildeqdist(const double a, const double b, const real_1d_array &y, const ae_int_t n, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuildeqdist(a, b, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant: generation of the model on equidistant grid.
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1]
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildeqdist(const double a, const double b, const real_1d_array &y, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = y.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuildeqdist(a, b, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant on Chebyshev grid (first kind).
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1],
|
|
Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n)))
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildcheb1(const double a, const double b, const real_1d_array &y, const ae_int_t n, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuildcheb1(a, b, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant on Chebyshev grid (first kind).
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1],
|
|
Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n)))
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildcheb1(const double a, const double b, const real_1d_array &y, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = y.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuildcheb1(a, b, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant on Chebyshev grid (second kind).
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1],
|
|
Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1)))
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildcheb2(const double a, const double b, const real_1d_array &y, const ae_int_t n, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuildcheb2(a, b, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant on Chebyshev grid (second kind).
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1],
|
|
Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1)))
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildcheb2(const double a, const double b, const real_1d_array &y, barycentricinterpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = y.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialbuildcheb2(a, b, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fast equidistant polynomial interpolation function with O(N) complexity
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on equidistant grid, N>=1
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolynomialBuildEqDist()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalceqdist(const double a, const double b, const real_1d_array &f, const ae_int_t n, const double t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::polynomialcalceqdist(a, b, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), n, t, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fast equidistant polynomial interpolation function with O(N) complexity
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on equidistant grid, N>=1
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolynomialBuildEqDist()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalceqdist(const double a, const double b, const real_1d_array &f, const double t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = f.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::polynomialcalceqdist(a, b, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), n, t, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fast polynomial interpolation function on Chebyshev points (first kind)
|
|
with O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on Chebyshev grid (first kind),
|
|
X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n))
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolIntBuildCheb1()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalccheb1(const double a, const double b, const real_1d_array &f, const ae_int_t n, const double t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::polynomialcalccheb1(a, b, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), n, t, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fast polynomial interpolation function on Chebyshev points (first kind)
|
|
with O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on Chebyshev grid (first kind),
|
|
X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n))
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolIntBuildCheb1()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalccheb1(const double a, const double b, const real_1d_array &f, const double t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = f.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::polynomialcalccheb1(a, b, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), n, t, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fast polynomial interpolation function on Chebyshev points (second kind)
|
|
with O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on Chebyshev grid (second kind),
|
|
X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1))
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolIntBuildCheb2()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalccheb2(const double a, const double b, const real_1d_array &f, const ae_int_t n, const double t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::polynomialcalccheb2(a, b, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), n, t, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fast polynomial interpolation function on Chebyshev points (second kind)
|
|
with O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on Chebyshev grid (second kind),
|
|
X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1))
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolIntBuildCheb2()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalccheb2(const double a, const double b, const real_1d_array &f, const double t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = f.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::polynomialcalccheb2(a, b, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), n, t, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
1-dimensional spline interpolant
|
|
*************************************************************************/
|
|
_spline1dinterpolant_owner::_spline1dinterpolant_owner()
|
|
{
|
|
p_struct = (alglib_impl::spline1dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline1dinterpolant_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline1dinterpolant_owner::_spline1dinterpolant_owner(const _spline1dinterpolant_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::spline1dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline1dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline1dinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline1dinterpolant_owner& _spline1dinterpolant_owner::operator=(const _spline1dinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_spline1dinterpolant_clear(p_struct);
|
|
if( !alglib_impl::_spline1dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline1dinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_spline1dinterpolant_owner::~_spline1dinterpolant_owner()
|
|
{
|
|
alglib_impl::_spline1dinterpolant_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::spline1dinterpolant* _spline1dinterpolant_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::spline1dinterpolant* _spline1dinterpolant_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::spline1dinterpolant*>(p_struct);
|
|
}
|
|
spline1dinterpolant::spline1dinterpolant() : _spline1dinterpolant_owner()
|
|
{
|
|
}
|
|
|
|
spline1dinterpolant::spline1dinterpolant(const spline1dinterpolant &rhs):_spline1dinterpolant_owner(rhs)
|
|
{
|
|
}
|
|
|
|
spline1dinterpolant& spline1dinterpolant::operator=(const spline1dinterpolant &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_spline1dinterpolant_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
spline1dinterpolant::~spline1dinterpolant()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds linear spline interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildlinear(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildlinear(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds linear spline interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildlinear(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dbuildlinear': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildlinear(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds cubic spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildcubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t boundltype, const double boundl, const ae_int_t boundrtype, const double boundr, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds cubic spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildcubic(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundltype;
|
|
double boundl;
|
|
ae_int_t boundrtype;
|
|
double boundr;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dbuildcubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
boundltype = 0;
|
|
boundl = 0;
|
|
boundrtype = 0;
|
|
boundr = 0;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at nodes x[], it calculates and returns table of function derivatives d[]
|
|
(calculated at the same nodes x[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes
|
|
Y - function values
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
D - derivative values at X[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Derivative values are correctly reordered on return, so D[I] is always
|
|
equal to S'(X[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dgriddiffcubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t boundltype, const double boundl, const ae_int_t boundrtype, const double boundr, real_1d_array &d)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dgriddiffcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(d.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at nodes x[], it calculates and returns table of function derivatives d[]
|
|
(calculated at the same nodes x[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes
|
|
Y - function values
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
D - derivative values at X[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Derivative values are correctly reordered on return, so D[I] is always
|
|
equal to S'(X[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dgriddiffcubic(const real_1d_array &x, const real_1d_array &y, real_1d_array &d)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundltype;
|
|
double boundl;
|
|
ae_int_t boundrtype;
|
|
double boundr;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dgriddiffcubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
boundltype = 0;
|
|
boundl = 0;
|
|
boundrtype = 0;
|
|
boundr = 0;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dgriddiffcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(d.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at nodes x[], it calculates and returns tables of first and second
|
|
function derivatives d1[] and d2[] (calculated at the same nodes x[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes
|
|
Y - function values
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
D1 - S' values at X[]
|
|
D2 - S'' values at X[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Derivative values are correctly reordered on return, so D[I] is always
|
|
equal to S'(X[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dgriddiff2cubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t boundltype, const double boundl, const ae_int_t boundrtype, const double boundr, real_1d_array &d1, real_1d_array &d2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dgriddiff2cubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(d1.c_ptr()), const_cast<alglib_impl::ae_vector*>(d2.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at nodes x[], it calculates and returns tables of first and second
|
|
function derivatives d1[] and d2[] (calculated at the same nodes x[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes
|
|
Y - function values
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
D1 - S' values at X[]
|
|
D2 - S'' values at X[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Derivative values are correctly reordered on return, so D[I] is always
|
|
equal to S'(X[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dgriddiff2cubic(const real_1d_array &x, const real_1d_array &y, real_1d_array &d1, real_1d_array &d2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundltype;
|
|
double boundl;
|
|
ae_int_t boundrtype;
|
|
double boundr;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dgriddiff2cubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
boundltype = 0;
|
|
boundl = 0;
|
|
boundrtype = 0;
|
|
boundr = 0;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dgriddiff2cubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(d1.c_ptr()), const_cast<alglib_impl::ae_vector*>(d2.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[] (calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvcubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t boundltype, const double boundl, const ae_int_t boundrtype, const double boundr, const real_1d_array &x2, const ae_int_t n2, real_1d_array &y2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dconvcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[] (calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvcubic(const real_1d_array &x, const real_1d_array &y, const real_1d_array &x2, real_1d_array &y2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundltype;
|
|
double boundl;
|
|
ae_int_t boundrtype;
|
|
double boundr;
|
|
ae_int_t n2;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dconvcubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
boundltype = 0;
|
|
boundl = 0;
|
|
boundrtype = 0;
|
|
boundr = 0;
|
|
n2 = x2.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dconvcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[] and derivatives d2[] (calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
D2 - first derivatives at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvdiffcubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t boundltype, const double boundl, const ae_int_t boundrtype, const double boundr, const real_1d_array &x2, const ae_int_t n2, real_1d_array &y2, real_1d_array &d2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dconvdiffcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), const_cast<alglib_impl::ae_vector*>(d2.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[] and derivatives d2[] (calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
D2 - first derivatives at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvdiffcubic(const real_1d_array &x, const real_1d_array &y, const real_1d_array &x2, real_1d_array &y2, real_1d_array &d2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundltype;
|
|
double boundl;
|
|
ae_int_t boundrtype;
|
|
double boundr;
|
|
ae_int_t n2;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dconvdiffcubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
boundltype = 0;
|
|
boundl = 0;
|
|
boundrtype = 0;
|
|
boundr = 0;
|
|
n2 = x2.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dconvdiffcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), const_cast<alglib_impl::ae_vector*>(d2.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[], first and second derivatives d2[] and dd2[]
|
|
(calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
D2 - first derivatives at X2[]
|
|
DD2 - second derivatives at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvdiff2cubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t boundltype, const double boundl, const ae_int_t boundrtype, const double boundr, const real_1d_array &x2, const ae_int_t n2, real_1d_array &y2, real_1d_array &d2, real_1d_array &dd2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dconvdiff2cubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), const_cast<alglib_impl::ae_vector*>(d2.c_ptr()), const_cast<alglib_impl::ae_vector*>(dd2.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[], first and second derivatives d2[] and dd2[]
|
|
(calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
D2 - first derivatives at X2[]
|
|
DD2 - second derivatives at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvdiff2cubic(const real_1d_array &x, const real_1d_array &y, const real_1d_array &x2, real_1d_array &y2, real_1d_array &d2, real_1d_array &dd2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundltype;
|
|
double boundl;
|
|
ae_int_t boundrtype;
|
|
double boundr;
|
|
ae_int_t n2;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dconvdiff2cubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
boundltype = 0;
|
|
boundl = 0;
|
|
boundrtype = 0;
|
|
boundr = 0;
|
|
n2 = x2.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dconvdiff2cubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundltype, boundl, boundrtype, boundr, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), const_cast<alglib_impl::ae_vector*>(d2.c_ptr()), const_cast<alglib_impl::ae_vector*>(dd2.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Catmull-Rom spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundType - boundary condition type:
|
|
* -1 for periodic boundary condition
|
|
* 0 for parabolically terminated spline (default)
|
|
Tension - tension parameter:
|
|
* tension=0 corresponds to classic Catmull-Rom spline (default)
|
|
* 0<tension<1 corresponds to more general form - cardinal spline
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildcatmullrom(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t boundtype, const double tension, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildcatmullrom(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundtype, tension, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Catmull-Rom spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundType - boundary condition type:
|
|
* -1 for periodic boundary condition
|
|
* 0 for parabolically terminated spline (default)
|
|
Tension - tension parameter:
|
|
* tension=0 corresponds to classic Catmull-Rom spline (default)
|
|
* 0<tension<1 corresponds to more general form - cardinal spline
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildcatmullrom(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundtype;
|
|
double tension;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dbuildcatmullrom': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
boundtype = 0;
|
|
tension = 0;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildcatmullrom(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, boundtype, tension, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Hermite spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
D - derivatives, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildhermite(const real_1d_array &x, const real_1d_array &y, const real_1d_array &d, const ae_int_t n, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildhermite(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(d.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Hermite spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
D - derivatives, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildhermite(const real_1d_array &x, const real_1d_array &y, const real_1d_array &d, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()) || (x.length()!=d.length()))
|
|
throw ap_error("Error while calling 'spline1dbuildhermite': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildhermite(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(d.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Akima spline interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildakima(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildakima(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Akima spline interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildakima(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dbuildakima': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildakima(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the spline at the given point X.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant
|
|
X - point
|
|
|
|
Result:
|
|
S(x)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline1dcalc(const spline1dinterpolant &c, const double x)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::spline1dcalc(const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), x, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine differentiates the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X - point
|
|
|
|
Result:
|
|
S - S(x)
|
|
DS - S'(x)
|
|
D2S - S''(x)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1ddiff(const spline1dinterpolant &c, const double x, double &s, double &ds, double &d2s)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1ddiff(const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), x, &s, &ds, &d2s, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine unpacks the spline into the coefficients table.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X - point
|
|
|
|
OUTPUT PARAMETERS:
|
|
Tbl - coefficients table, unpacked format, array[0..N-2, 0..5].
|
|
For I = 0...N-2:
|
|
Tbl[I,0] = X[i]
|
|
Tbl[I,1] = X[i+1]
|
|
Tbl[I,2] = C0
|
|
Tbl[I,3] = C1
|
|
Tbl[I,4] = C2
|
|
Tbl[I,5] = C3
|
|
On [x[i], x[i+1]] spline is equals to:
|
|
S(x) = C0 + C1*t + C2*t^2 + C3*t^3
|
|
t = x-x[i]
|
|
|
|
NOTE:
|
|
You can rebuild spline with Spline1DBuildHermite() function, which
|
|
accepts as inputs function values and derivatives at nodes, which are
|
|
easy to calculate when you have coefficients.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dunpack(const spline1dinterpolant &c, ae_int_t &n, real_2d_array &tbl)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dunpack(const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &n, const_cast<alglib_impl::ae_matrix*>(tbl.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline argument.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: x = A*t + B
|
|
Result:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dlintransx(const spline1dinterpolant &c, const double a, const double b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dlintransx(const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), a, b, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: S2(x) = A*S(x) + B
|
|
Result:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dlintransy(const spline1dinterpolant &c, const double a, const double b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dlintransy(const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), a, b, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine integrates the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X - right bound of the integration interval [a, x],
|
|
here 'a' denotes min(x[])
|
|
Result:
|
|
integral(S(t)dt,a,x)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline1dintegrate(const spline1dinterpolant &c, const double x)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::spline1dintegrate(const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), x, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function builds monotone cubic Hermite interpolant. This interpolant
|
|
is monotonic in [x(0),x(n-1)] and is constant outside of this interval.
|
|
|
|
In case y[] form non-monotonic sequence, interpolant is piecewise
|
|
monotonic. Say, for x=(0,1,2,3,4) and y=(0,1,2,1,0) interpolant will
|
|
monotonically grow at [0..2] and monotonically decrease at [2..4].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]. Subroutine automatically
|
|
sorts points, so caller may pass unsorted array.
|
|
Y - function values, array[0..N-1]
|
|
N - the number of points(N>=2).
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 21.06.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildmonotone(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildmonotone(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function builds monotone cubic Hermite interpolant. This interpolant
|
|
is monotonic in [x(0),x(n-1)] and is constant outside of this interval.
|
|
|
|
In case y[] form non-monotonic sequence, interpolant is piecewise
|
|
monotonic. Say, for x=(0,1,2,3,4) and y=(0,1,2,1,0) interpolant will
|
|
monotonically grow at [0..2] and monotonically decrease at [2..4].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]. Subroutine automatically
|
|
sorts points, so caller may pass unsorted array.
|
|
Y - function values, array[0..N-1]
|
|
N - the number of points(N>=2).
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 21.06.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildmonotone(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dbuildmonotone': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dbuildmonotone(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, const_cast<alglib_impl::spline1dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Polynomial fitting report:
|
|
TaskRCond reciprocal of task's condition number
|
|
RMSError RMS error
|
|
AvgError average error
|
|
AvgRelError average relative error (for non-zero Y[I])
|
|
MaxError maximum error
|
|
*************************************************************************/
|
|
_polynomialfitreport_owner::_polynomialfitreport_owner()
|
|
{
|
|
p_struct = (alglib_impl::polynomialfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::polynomialfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_polynomialfitreport_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_polynomialfitreport_owner::_polynomialfitreport_owner(const _polynomialfitreport_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::polynomialfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::polynomialfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_polynomialfitreport_init_copy(p_struct, const_cast<alglib_impl::polynomialfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_polynomialfitreport_owner& _polynomialfitreport_owner::operator=(const _polynomialfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_polynomialfitreport_clear(p_struct);
|
|
if( !alglib_impl::_polynomialfitreport_init_copy(p_struct, const_cast<alglib_impl::polynomialfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_polynomialfitreport_owner::~_polynomialfitreport_owner()
|
|
{
|
|
alglib_impl::_polynomialfitreport_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::polynomialfitreport* _polynomialfitreport_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::polynomialfitreport* _polynomialfitreport_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::polynomialfitreport*>(p_struct);
|
|
}
|
|
polynomialfitreport::polynomialfitreport() : _polynomialfitreport_owner() ,taskrcond(p_struct->taskrcond),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror)
|
|
{
|
|
}
|
|
|
|
polynomialfitreport::polynomialfitreport(const polynomialfitreport &rhs):_polynomialfitreport_owner(rhs) ,taskrcond(p_struct->taskrcond),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror)
|
|
{
|
|
}
|
|
|
|
polynomialfitreport& polynomialfitreport::operator=(const polynomialfitreport &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_polynomialfitreport_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
polynomialfitreport::~polynomialfitreport()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Barycentric fitting report:
|
|
RMSError RMS error
|
|
AvgError average error
|
|
AvgRelError average relative error (for non-zero Y[I])
|
|
MaxError maximum error
|
|
TaskRCond reciprocal of task's condition number
|
|
*************************************************************************/
|
|
_barycentricfitreport_owner::_barycentricfitreport_owner()
|
|
{
|
|
p_struct = (alglib_impl::barycentricfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_barycentricfitreport_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_barycentricfitreport_owner::_barycentricfitreport_owner(const _barycentricfitreport_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::barycentricfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_barycentricfitreport_init_copy(p_struct, const_cast<alglib_impl::barycentricfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_barycentricfitreport_owner& _barycentricfitreport_owner::operator=(const _barycentricfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_barycentricfitreport_clear(p_struct);
|
|
if( !alglib_impl::_barycentricfitreport_init_copy(p_struct, const_cast<alglib_impl::barycentricfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_barycentricfitreport_owner::~_barycentricfitreport_owner()
|
|
{
|
|
alglib_impl::_barycentricfitreport_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::barycentricfitreport* _barycentricfitreport_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::barycentricfitreport* _barycentricfitreport_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::barycentricfitreport*>(p_struct);
|
|
}
|
|
barycentricfitreport::barycentricfitreport() : _barycentricfitreport_owner() ,taskrcond(p_struct->taskrcond),dbest(p_struct->dbest),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror)
|
|
{
|
|
}
|
|
|
|
barycentricfitreport::barycentricfitreport(const barycentricfitreport &rhs):_barycentricfitreport_owner(rhs) ,taskrcond(p_struct->taskrcond),dbest(p_struct->dbest),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror)
|
|
{
|
|
}
|
|
|
|
barycentricfitreport& barycentricfitreport::operator=(const barycentricfitreport &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_barycentricfitreport_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
barycentricfitreport::~barycentricfitreport()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Spline fitting report:
|
|
RMSError RMS error
|
|
AvgError average error
|
|
AvgRelError average relative error (for non-zero Y[I])
|
|
MaxError maximum error
|
|
|
|
Fields below are filled by obsolete functions (Spline1DFitCubic,
|
|
Spline1DFitHermite). Modern fitting functions do NOT fill these fields:
|
|
TaskRCond reciprocal of task's condition number
|
|
*************************************************************************/
|
|
_spline1dfitreport_owner::_spline1dfitreport_owner()
|
|
{
|
|
p_struct = (alglib_impl::spline1dfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline1dfitreport_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline1dfitreport_owner::_spline1dfitreport_owner(const _spline1dfitreport_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::spline1dfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline1dfitreport_init_copy(p_struct, const_cast<alglib_impl::spline1dfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline1dfitreport_owner& _spline1dfitreport_owner::operator=(const _spline1dfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_spline1dfitreport_clear(p_struct);
|
|
if( !alglib_impl::_spline1dfitreport_init_copy(p_struct, const_cast<alglib_impl::spline1dfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_spline1dfitreport_owner::~_spline1dfitreport_owner()
|
|
{
|
|
alglib_impl::_spline1dfitreport_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::spline1dfitreport* _spline1dfitreport_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::spline1dfitreport* _spline1dfitreport_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::spline1dfitreport*>(p_struct);
|
|
}
|
|
spline1dfitreport::spline1dfitreport() : _spline1dfitreport_owner() ,taskrcond(p_struct->taskrcond),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror)
|
|
{
|
|
}
|
|
|
|
spline1dfitreport::spline1dfitreport(const spline1dfitreport &rhs):_spline1dfitreport_owner(rhs) ,taskrcond(p_struct->taskrcond),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror)
|
|
{
|
|
}
|
|
|
|
spline1dfitreport& spline1dfitreport::operator=(const spline1dfitreport &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_spline1dfitreport_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
spline1dfitreport::~spline1dfitreport()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Least squares fitting report. This structure contains informational fields
|
|
which are set by fitting functions provided by this unit.
|
|
|
|
Different functions initialize different sets of fields, so you should
|
|
read documentation on specific function you used in order to know which
|
|
fields are initialized.
|
|
|
|
TaskRCond reciprocal of task's condition number
|
|
IterationsCount number of internal iterations
|
|
|
|
VarIdx if user-supplied gradient contains errors which were
|
|
detected by nonlinear fitter, this field is set to
|
|
index of the first component of gradient which is
|
|
suspected to be spoiled by bugs.
|
|
|
|
RMSError RMS error
|
|
AvgError average error
|
|
AvgRelError average relative error (for non-zero Y[I])
|
|
MaxError maximum error
|
|
|
|
WRMSError weighted RMS error
|
|
|
|
CovPar covariance matrix for parameters, filled by some solvers
|
|
ErrPar vector of errors in parameters, filled by some solvers
|
|
ErrCurve vector of fit errors - variability of the best-fit
|
|
curve, filled by some solvers.
|
|
Noise vector of per-point noise estimates, filled by
|
|
some solvers.
|
|
R2 coefficient of determination (non-weighted, non-adjusted),
|
|
filled by some solvers.
|
|
*************************************************************************/
|
|
_lsfitreport_owner::_lsfitreport_owner()
|
|
{
|
|
p_struct = (alglib_impl::lsfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_lsfitreport_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_lsfitreport_owner::_lsfitreport_owner(const _lsfitreport_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::lsfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_lsfitreport_init_copy(p_struct, const_cast<alglib_impl::lsfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_lsfitreport_owner& _lsfitreport_owner::operator=(const _lsfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_lsfitreport_clear(p_struct);
|
|
if( !alglib_impl::_lsfitreport_init_copy(p_struct, const_cast<alglib_impl::lsfitreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_lsfitreport_owner::~_lsfitreport_owner()
|
|
{
|
|
alglib_impl::_lsfitreport_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::lsfitreport* _lsfitreport_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::lsfitreport* _lsfitreport_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::lsfitreport*>(p_struct);
|
|
}
|
|
lsfitreport::lsfitreport() : _lsfitreport_owner() ,taskrcond(p_struct->taskrcond),iterationscount(p_struct->iterationscount),varidx(p_struct->varidx),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror),wrmserror(p_struct->wrmserror),covpar(&p_struct->covpar),errpar(&p_struct->errpar),errcurve(&p_struct->errcurve),noise(&p_struct->noise),r2(p_struct->r2)
|
|
{
|
|
}
|
|
|
|
lsfitreport::lsfitreport(const lsfitreport &rhs):_lsfitreport_owner(rhs) ,taskrcond(p_struct->taskrcond),iterationscount(p_struct->iterationscount),varidx(p_struct->varidx),rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),avgrelerror(p_struct->avgrelerror),maxerror(p_struct->maxerror),wrmserror(p_struct->wrmserror),covpar(&p_struct->covpar),errpar(&p_struct->errpar),errcurve(&p_struct->errcurve),noise(&p_struct->noise),r2(p_struct->r2)
|
|
{
|
|
}
|
|
|
|
lsfitreport& lsfitreport::operator=(const lsfitreport &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_lsfitreport_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
lsfitreport::~lsfitreport()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear fitter.
|
|
|
|
You should use ALGLIB functions to work with fitter.
|
|
Never try to access its fields directly!
|
|
*************************************************************************/
|
|
_lsfitstate_owner::_lsfitstate_owner()
|
|
{
|
|
p_struct = (alglib_impl::lsfitstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitstate), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_lsfitstate_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_lsfitstate_owner::_lsfitstate_owner(const _lsfitstate_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::lsfitstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitstate), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_lsfitstate_init_copy(p_struct, const_cast<alglib_impl::lsfitstate*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_lsfitstate_owner& _lsfitstate_owner::operator=(const _lsfitstate_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_lsfitstate_clear(p_struct);
|
|
if( !alglib_impl::_lsfitstate_init_copy(p_struct, const_cast<alglib_impl::lsfitstate*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_lsfitstate_owner::~_lsfitstate_owner()
|
|
{
|
|
alglib_impl::_lsfitstate_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::lsfitstate* _lsfitstate_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::lsfitstate* _lsfitstate_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::lsfitstate*>(p_struct);
|
|
}
|
|
lsfitstate::lsfitstate() : _lsfitstate_owner() ,needf(p_struct->needf),needfg(p_struct->needfg),needfgh(p_struct->needfgh),xupdated(p_struct->xupdated),c(&p_struct->c),f(p_struct->f),g(&p_struct->g),h(&p_struct->h),x(&p_struct->x)
|
|
{
|
|
}
|
|
|
|
lsfitstate::lsfitstate(const lsfitstate &rhs):_lsfitstate_owner(rhs) ,needf(p_struct->needf),needfg(p_struct->needfg),needfgh(p_struct->needfgh),xupdated(p_struct->xupdated),c(&p_struct->c),f(p_struct->f),g(&p_struct->g),h(&p_struct->h),x(&p_struct->x)
|
|
{
|
|
}
|
|
|
|
lsfitstate& lsfitstate::operator=(const lsfitstate &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_lsfitstate_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
lsfitstate::~lsfitstate()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fitting by polynomials in barycentric form. This function provides simple
|
|
unterface for unconstrained unweighted fitting. See PolynomialFitWC() if
|
|
you need constrained fitting.
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO:
|
|
PolynomialFitWC()
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0
|
|
* if given, only leading N elements of X/Y are used
|
|
* if not given, automatically determined from sizes of X/Y
|
|
M - number of basis functions (= polynomial_degree + 1), M>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
P - interpolant in barycentric form.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialfit(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t m, ae_int_t &info, barycentricinterpolant &p, polynomialfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialfit(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, &info, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), const_cast<alglib_impl::polynomialfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fitting by polynomials in barycentric form. This function provides simple
|
|
unterface for unconstrained unweighted fitting. See PolynomialFitWC() if
|
|
you need constrained fitting.
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO:
|
|
PolynomialFitWC()
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0
|
|
* if given, only leading N elements of X/Y are used
|
|
* if not given, automatically determined from sizes of X/Y
|
|
M - number of basis functions (= polynomial_degree + 1), M>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
P - interpolant in barycentric form.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialfit(const real_1d_array &x, const real_1d_array &y, const ae_int_t m, ae_int_t &info, barycentricinterpolant &p, polynomialfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'polynomialfit': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialfit(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, &info, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), const_cast<alglib_impl::polynomialfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by polynomials in barycentric form, with constraints on
|
|
function values or first derivatives.
|
|
|
|
Small regularizing term is used when solving constrained tasks (to improve
|
|
stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO:
|
|
PolynomialFit()
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points, N>0.
|
|
* if given, only leading N elements of X/Y/W are used
|
|
* if not given, automatically determined from sizes of X/Y/W
|
|
XC - points where polynomial values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that P(XC[i])=YC[i]
|
|
* DC[i]=1 means that P'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints, 0<=K<M.
|
|
K=0 means no constraints (XC/YC/DC are not used in such cases)
|
|
M - number of basis functions (= polynomial_degree + 1), M>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
P - interpolant in barycentric form.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* even simple constraints can be inconsistent, see Wikipedia article on
|
|
this subject: http://en.wikipedia.org/wiki/Birkhoff_interpolation
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints is NOT GUARANTEED.
|
|
* in the one special cases, however, we can guarantee consistency. This
|
|
case is: M>1 and constraints on the function values (NOT DERIVATIVES)
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY when you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialfitwc(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const ae_int_t n, const real_1d_array &xc, const real_1d_array &yc, const integer_1d_array &dc, const ae_int_t k, const ae_int_t m, ae_int_t &info, barycentricinterpolant &p, polynomialfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialfitwc(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(xc.c_ptr()), const_cast<alglib_impl::ae_vector*>(yc.c_ptr()), const_cast<alglib_impl::ae_vector*>(dc.c_ptr()), k, m, &info, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), const_cast<alglib_impl::polynomialfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by polynomials in barycentric form, with constraints on
|
|
function values or first derivatives.
|
|
|
|
Small regularizing term is used when solving constrained tasks (to improve
|
|
stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO:
|
|
PolynomialFit()
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points, N>0.
|
|
* if given, only leading N elements of X/Y/W are used
|
|
* if not given, automatically determined from sizes of X/Y/W
|
|
XC - points where polynomial values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that P(XC[i])=YC[i]
|
|
* DC[i]=1 means that P'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints, 0<=K<M.
|
|
K=0 means no constraints (XC/YC/DC are not used in such cases)
|
|
M - number of basis functions (= polynomial_degree + 1), M>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
P - interpolant in barycentric form.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* even simple constraints can be inconsistent, see Wikipedia article on
|
|
this subject: http://en.wikipedia.org/wiki/Birkhoff_interpolation
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints is NOT GUARANTEED.
|
|
* in the one special cases, however, we can guarantee consistency. This
|
|
case is: M>1 and constraints on the function values (NOT DERIVATIVES)
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY when you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialfitwc(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &xc, const real_1d_array &yc, const integer_1d_array &dc, const ae_int_t m, ae_int_t &info, barycentricinterpolant &p, polynomialfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t k;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
throw ap_error("Error while calling 'polynomialfitwc': looks like one of arguments has wrong size");
|
|
if( (xc.length()!=yc.length()) || (xc.length()!=dc.length()))
|
|
throw ap_error("Error while calling 'polynomialfitwc': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
k = xc.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::polynomialfitwc(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(xc.c_ptr()), const_cast<alglib_impl::ae_vector*>(yc.c_ptr()), const_cast<alglib_impl::ae_vector*>(dc.c_ptr()), k, m, &info, const_cast<alglib_impl::barycentricinterpolant*>(p.c_ptr()), const_cast<alglib_impl::polynomialfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weghted rational least squares fitting using Floater-Hormann rational
|
|
functions with optimal D chosen from [0,9], with constraints and
|
|
individual weights.
|
|
|
|
Equidistant grid with M node on [min(x),max(x)] is used to build basis
|
|
functions. Different values of D are tried, optimal D (least WEIGHTED root
|
|
mean square error) is chosen. Task is linear, so linear least squares
|
|
solver is used. Complexity of this computational scheme is O(N*M^2)
|
|
(mostly dominated by the least squares solver).
|
|
|
|
SEE ALSO
|
|
* BarycentricFitFloaterHormann(), "lightweight" fitting without invididual
|
|
weights and constraints.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points, N>0.
|
|
XC - points where function values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints, 0<=K<M.
|
|
K=0 means no constraints (XC/YC/DC are not used in such cases)
|
|
M - number of basis functions ( = number_of_nodes), M>=2.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
-1 means another errors in parameters passed
|
|
(N<=0, for example)
|
|
B - barycentric interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* DBest best value of the D parameter
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroutine doesn't calculate task's condition number for K<>0.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained barycentric interpolants:
|
|
* excessive constraints can be inconsistent. Floater-Hormann basis
|
|
functions aren't as flexible as splines (although they are very smooth).
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints IS NOT GUARANTEED.
|
|
* in the several special cases, however, we CAN guarantee consistency.
|
|
* one of this cases is constraints on the function VALUES at the interval
|
|
boundaries. Note that consustency of the constraints on the function
|
|
DERIVATIVES is NOT guaranteed (you can use in such cases cubic splines
|
|
which are more flexible).
|
|
* another special case is ONE constraint on the function value (OR, but
|
|
not AND, derivative) anywhere in the interval
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY WHEN you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricfitfloaterhormannwc(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const ae_int_t n, const real_1d_array &xc, const real_1d_array &yc, const integer_1d_array &dc, const ae_int_t k, const ae_int_t m, ae_int_t &info, barycentricinterpolant &b, barycentricfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentricfitfloaterhormannwc(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(xc.c_ptr()), const_cast<alglib_impl::ae_vector*>(yc.c_ptr()), const_cast<alglib_impl::ae_vector*>(dc.c_ptr()), k, m, &info, const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), const_cast<alglib_impl::barycentricfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Rational least squares fitting using Floater-Hormann rational functions
|
|
with optimal D chosen from [0,9].
|
|
|
|
Equidistant grid with M node on [min(x),max(x)] is used to build basis
|
|
functions. Different values of D are tried, optimal D (least root mean
|
|
square error) is chosen. Task is linear, so linear least squares solver
|
|
is used. Complexity of this computational scheme is O(N*M^2) (mostly
|
|
dominated by the least squares solver).
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0.
|
|
M - number of basis functions ( = number_of_nodes), M>=2.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
B - barycentric interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* DBest best value of the D parameter
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricfitfloaterhormann(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t m, ae_int_t &info, barycentricinterpolant &b, barycentricfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::barycentricfitfloaterhormann(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, &info, const_cast<alglib_impl::barycentricinterpolant*>(b.c_ptr()), const_cast<alglib_impl::barycentricfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Rational least squares fitting using Floater-Hormann rational functions
|
|
with optimal D chosen from [0,9].
|
|
|
|
Equidistant grid with M node on [min(x),max(x)] is used to build basis
|
|
functions. Different values of D are tried, optimal D (least root mean
|
|
square error) is chosen. Task is linear, so linear least squares solver
|
|
is used. Complexity of this computational scheme is O(N*M^2) (mostly
|
|
dominated by the least squares solver).
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0.
|
|
M - number of basis functions ( = number_of_nodes), M>=2.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
B - barycentric interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* DBest best value of the D parameter
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitpenalized(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t m, const double rho, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitpenalized(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, rho, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Rational least squares fitting using Floater-Hormann rational functions
|
|
with optimal D chosen from [0,9].
|
|
|
|
Equidistant grid with M node on [min(x),max(x)] is used to build basis
|
|
functions. Different values of D are tried, optimal D (least root mean
|
|
square error) is chosen. Task is linear, so linear least squares solver
|
|
is used. Complexity of this computational scheme is O(N*M^2) (mostly
|
|
dominated by the least squares solver).
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0.
|
|
M - number of basis functions ( = number_of_nodes), M>=2.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
B - barycentric interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* DBest best value of the D parameter
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitpenalized(const real_1d_array &x, const real_1d_array &y, const ae_int_t m, const double rho, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dfitpenalized': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitpenalized(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, rho, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by penalized cubic spline.
|
|
|
|
Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are cubic splines with natural boundary
|
|
conditions. Problem is regularized by adding non-linearity penalty to the
|
|
usual least squares penalty function:
|
|
|
|
S(x) = arg min { LS + P }, where
|
|
LS = SUM { w[i]^2*(y[i] - S(x[i]))^2 } - least squares penalty
|
|
P = C*10^rho*integral{ S''(x)^2*dx } - non-linearity penalty
|
|
rho - tunable constant given by user
|
|
C - automatically determined scale parameter,
|
|
makes penalty invariant with respect to scaling of X, Y, W.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
problem.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
M - number of basis functions ( = number_of_nodes), M>=4.
|
|
Rho - regularization constant passed by user. It penalizes
|
|
nonlinearity in the regression spline. It is logarithmically
|
|
scaled, i.e. actual value of regularization constant is
|
|
calculated as 10^Rho. It is automatically scaled so that:
|
|
* Rho=2.0 corresponds to moderate amount of nonlinearity
|
|
* generally, it should be somewhere in the [-8.0,+8.0]
|
|
If you do not want to penalize nonlineary,
|
|
pass small Rho. Values as low as -15 should work.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD or
|
|
Cholesky decomposition; problem may be
|
|
too ill-conditioned (very rare)
|
|
S - spline interpolant.
|
|
Rep - Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
NOTE 1: additional nodes are added to the spline outside of the fitting
|
|
interval to force linearity when x<min(x,xc) or x>max(x,xc). It is done
|
|
for consistency - we penalize non-linearity at [min(x,xc),max(x,xc)], so
|
|
it is natural to force linearity outside of this interval.
|
|
|
|
NOTE 2: function automatically sorts points, so caller may pass unsorted
|
|
array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.10.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitpenalizedw(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const ae_int_t n, const ae_int_t m, const double rho, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitpenalizedw(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, m, rho, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by penalized cubic spline.
|
|
|
|
Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are cubic splines with natural boundary
|
|
conditions. Problem is regularized by adding non-linearity penalty to the
|
|
usual least squares penalty function:
|
|
|
|
S(x) = arg min { LS + P }, where
|
|
LS = SUM { w[i]^2*(y[i] - S(x[i]))^2 } - least squares penalty
|
|
P = C*10^rho*integral{ S''(x)^2*dx } - non-linearity penalty
|
|
rho - tunable constant given by user
|
|
C - automatically determined scale parameter,
|
|
makes penalty invariant with respect to scaling of X, Y, W.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
problem.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
M - number of basis functions ( = number_of_nodes), M>=4.
|
|
Rho - regularization constant passed by user. It penalizes
|
|
nonlinearity in the regression spline. It is logarithmically
|
|
scaled, i.e. actual value of regularization constant is
|
|
calculated as 10^Rho. It is automatically scaled so that:
|
|
* Rho=2.0 corresponds to moderate amount of nonlinearity
|
|
* generally, it should be somewhere in the [-8.0,+8.0]
|
|
If you do not want to penalize nonlineary,
|
|
pass small Rho. Values as low as -15 should work.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD or
|
|
Cholesky decomposition; problem may be
|
|
too ill-conditioned (very rare)
|
|
S - spline interpolant.
|
|
Rep - Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
NOTE 1: additional nodes are added to the spline outside of the fitting
|
|
interval to force linearity when x<min(x,xc) or x>max(x,xc). It is done
|
|
for consistency - we penalize non-linearity at [min(x,xc),max(x,xc)], so
|
|
it is natural to force linearity outside of this interval.
|
|
|
|
NOTE 2: function automatically sorts points, so caller may pass unsorted
|
|
array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.10.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitpenalizedw(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const ae_int_t m, const double rho, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
throw ap_error("Error while calling 'spline1dfitpenalizedw': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitpenalizedw(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, m, rho, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by cubic spline, with constraints on function values or
|
|
derivatives.
|
|
|
|
Equidistant grid with M-2 nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are cubic splines with continuous second
|
|
derivatives and non-fixed first derivatives at interval ends. Small
|
|
regularizing term is used when solving constrained tasks (to improve
|
|
stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO
|
|
Spline1DFitHermiteWC() - fitting by Hermite splines (more flexible,
|
|
less smooth)
|
|
Spline1DFitCubic() - "lightweight" fitting by cubic splines,
|
|
without invididual weights and constraints
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
XC - points where spline values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints (optional):
|
|
* 0<=K<M.
|
|
* K=0 means no constraints (XC/YC/DC are not used)
|
|
* if given, only first K elements of XC/YC/DC are used
|
|
* if not given, automatically determined from XC/YC/DC
|
|
M - number of basis functions ( = number_of_nodes+2), M>=4.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
S - spline interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* excessive constraints can be inconsistent. Splines are piecewise cubic
|
|
functions, and it is easy to create an example, where large number of
|
|
constraints concentrated in small area will result in inconsistency.
|
|
Just because spline is not flexible enough to satisfy all of them. And
|
|
same constraints spread across the [min(x),max(x)] will be perfectly
|
|
consistent.
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints IS NOT GUARANTEED.
|
|
* in the several special cases, however, we CAN guarantee consistency.
|
|
* one of this cases is constraints on the function values AND/OR its
|
|
derivatives at the interval boundaries.
|
|
* another special case is ONE constraint on the function value (OR, but
|
|
not AND, derivative) anywhere in the interval
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY WHEN you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitcubicwc(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const ae_int_t n, const real_1d_array &xc, const real_1d_array &yc, const integer_1d_array &dc, const ae_int_t k, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitcubicwc(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(xc.c_ptr()), const_cast<alglib_impl::ae_vector*>(yc.c_ptr()), const_cast<alglib_impl::ae_vector*>(dc.c_ptr()), k, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by cubic spline, with constraints on function values or
|
|
derivatives.
|
|
|
|
Equidistant grid with M-2 nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are cubic splines with continuous second
|
|
derivatives and non-fixed first derivatives at interval ends. Small
|
|
regularizing term is used when solving constrained tasks (to improve
|
|
stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO
|
|
Spline1DFitHermiteWC() - fitting by Hermite splines (more flexible,
|
|
less smooth)
|
|
Spline1DFitCubic() - "lightweight" fitting by cubic splines,
|
|
without invididual weights and constraints
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
XC - points where spline values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints (optional):
|
|
* 0<=K<M.
|
|
* K=0 means no constraints (XC/YC/DC are not used)
|
|
* if given, only first K elements of XC/YC/DC are used
|
|
* if not given, automatically determined from XC/YC/DC
|
|
M - number of basis functions ( = number_of_nodes+2), M>=4.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
S - spline interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* excessive constraints can be inconsistent. Splines are piecewise cubic
|
|
functions, and it is easy to create an example, where large number of
|
|
constraints concentrated in small area will result in inconsistency.
|
|
Just because spline is not flexible enough to satisfy all of them. And
|
|
same constraints spread across the [min(x),max(x)] will be perfectly
|
|
consistent.
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints IS NOT GUARANTEED.
|
|
* in the several special cases, however, we CAN guarantee consistency.
|
|
* one of this cases is constraints on the function values AND/OR its
|
|
derivatives at the interval boundaries.
|
|
* another special case is ONE constraint on the function value (OR, but
|
|
not AND, derivative) anywhere in the interval
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY WHEN you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitcubicwc(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &xc, const real_1d_array &yc, const integer_1d_array &dc, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t k;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
throw ap_error("Error while calling 'spline1dfitcubicwc': looks like one of arguments has wrong size");
|
|
if( (xc.length()!=yc.length()) || (xc.length()!=dc.length()))
|
|
throw ap_error("Error while calling 'spline1dfitcubicwc': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
k = xc.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitcubicwc(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(xc.c_ptr()), const_cast<alglib_impl::ae_vector*>(yc.c_ptr()), const_cast<alglib_impl::ae_vector*>(dc.c_ptr()), k, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by Hermite spline, with constraints on function values
|
|
or first derivatives.
|
|
|
|
Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are Hermite splines. Small regularizing
|
|
term is used when solving constrained tasks (to improve stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO
|
|
Spline1DFitCubicWC() - fitting by Cubic splines (less flexible,
|
|
more smooth)
|
|
Spline1DFitHermite() - "lightweight" Hermite fitting, without
|
|
invididual weights and constraints
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
XC - points where spline values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints (optional):
|
|
* 0<=K<M.
|
|
* K=0 means no constraints (XC/YC/DC are not used)
|
|
* if given, only first K elements of XC/YC/DC are used
|
|
* if not given, automatically determined from XC/YC/DC
|
|
M - number of basis functions (= 2 * number of nodes),
|
|
M>=4,
|
|
M IS EVEN!
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
-2 means odd M was passed (which is not supported)
|
|
-1 means another errors in parameters passed
|
|
(N<=0, for example)
|
|
S - spline interpolant.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
IMPORTANT:
|
|
this subroitine supports only even M's
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* excessive constraints can be inconsistent. Splines are piecewise cubic
|
|
functions, and it is easy to create an example, where large number of
|
|
constraints concentrated in small area will result in inconsistency.
|
|
Just because spline is not flexible enough to satisfy all of them. And
|
|
same constraints spread across the [min(x),max(x)] will be perfectly
|
|
consistent.
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints is NOT GUARANTEED.
|
|
* in the several special cases, however, we can guarantee consistency.
|
|
* one of this cases is M>=4 and constraints on the function value
|
|
(AND/OR its derivative) at the interval boundaries.
|
|
* another special case is M>=4 and ONE constraint on the function value
|
|
(OR, BUT NOT AND, derivative) anywhere in [min(x),max(x)]
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY when you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfithermitewc(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const ae_int_t n, const real_1d_array &xc, const real_1d_array &yc, const integer_1d_array &dc, const ae_int_t k, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfithermitewc(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(xc.c_ptr()), const_cast<alglib_impl::ae_vector*>(yc.c_ptr()), const_cast<alglib_impl::ae_vector*>(dc.c_ptr()), k, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by Hermite spline, with constraints on function values
|
|
or first derivatives.
|
|
|
|
Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are Hermite splines. Small regularizing
|
|
term is used when solving constrained tasks (to improve stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO
|
|
Spline1DFitCubicWC() - fitting by Cubic splines (less flexible,
|
|
more smooth)
|
|
Spline1DFitHermite() - "lightweight" Hermite fitting, without
|
|
invididual weights and constraints
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
XC - points where spline values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints (optional):
|
|
* 0<=K<M.
|
|
* K=0 means no constraints (XC/YC/DC are not used)
|
|
* if given, only first K elements of XC/YC/DC are used
|
|
* if not given, automatically determined from XC/YC/DC
|
|
M - number of basis functions (= 2 * number of nodes),
|
|
M>=4,
|
|
M IS EVEN!
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
-2 means odd M was passed (which is not supported)
|
|
-1 means another errors in parameters passed
|
|
(N<=0, for example)
|
|
S - spline interpolant.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
IMPORTANT:
|
|
this subroitine supports only even M's
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* excessive constraints can be inconsistent. Splines are piecewise cubic
|
|
functions, and it is easy to create an example, where large number of
|
|
constraints concentrated in small area will result in inconsistency.
|
|
Just because spline is not flexible enough to satisfy all of them. And
|
|
same constraints spread across the [min(x),max(x)] will be perfectly
|
|
consistent.
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints is NOT GUARANTEED.
|
|
* in the several special cases, however, we can guarantee consistency.
|
|
* one of this cases is M>=4 and constraints on the function value
|
|
(AND/OR its derivative) at the interval boundaries.
|
|
* another special case is M>=4 and ONE constraint on the function value
|
|
(OR, BUT NOT AND, derivative) anywhere in [min(x),max(x)]
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY when you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfithermitewc(const real_1d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &xc, const real_1d_array &yc, const integer_1d_array &dc, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t k;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
throw ap_error("Error while calling 'spline1dfithermitewc': looks like one of arguments has wrong size");
|
|
if( (xc.length()!=yc.length()) || (xc.length()!=dc.length()))
|
|
throw ap_error("Error while calling 'spline1dfithermitewc': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
k = xc.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfithermitewc(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(xc.c_ptr()), const_cast<alglib_impl::ae_vector*>(yc.c_ptr()), const_cast<alglib_impl::ae_vector*>(dc.c_ptr()), k, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Least squares fitting by cubic spline.
|
|
|
|
This subroutine is "lightweight" alternative for more complex and feature-
|
|
rich Spline1DFitCubicWC(). See Spline1DFitCubicWC() for more information
|
|
about subroutine parameters (we don't duplicate it here because of length)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitcubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Least squares fitting by cubic spline.
|
|
|
|
This subroutine is "lightweight" alternative for more complex and feature-
|
|
rich Spline1DFitCubicWC(). See Spline1DFitCubicWC() for more information
|
|
about subroutine parameters (we don't duplicate it here because of length)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitcubic(const real_1d_array &x, const real_1d_array &y, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dfitcubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfitcubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Least squares fitting by Hermite spline.
|
|
|
|
This subroutine is "lightweight" alternative for more complex and feature-
|
|
rich Spline1DFitHermiteWC(). See Spline1DFitHermiteWC() description for
|
|
more information about subroutine parameters (we don't duplicate it here
|
|
because of length).
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfithermite(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfithermite(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Least squares fitting by Hermite spline.
|
|
|
|
This subroutine is "lightweight" alternative for more complex and feature-
|
|
rich Spline1DFitHermiteWC(). See Spline1DFitHermiteWC() description for
|
|
more information about subroutine parameters (we don't duplicate it here
|
|
because of length).
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfithermite(const real_1d_array &x, const real_1d_array &y, const ae_int_t m, ae_int_t &info, spline1dinterpolant &s, spline1dfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
throw ap_error("Error while calling 'spline1dfithermite': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline1dfithermite(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, &info, const_cast<alglib_impl::spline1dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline1dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted linear least squares fitting.
|
|
|
|
QR decomposition is used to reduce task to MxM, then triangular solver or
|
|
SVD-based solver is used depending on condition number of the system. It
|
|
allows to maximize speed and retain decent accuracy.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
W - array[0..N-1] Weights corresponding to function values.
|
|
Each summand in square sum of approximation deviations
|
|
from given values is multiplied by the square of
|
|
corresponding weight.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I, J] - value of J-th basis function in I-th point.
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -1 incorrect N/M were specified
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* Rep.TaskRCond reciprocal of condition number
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearw(const real_1d_array &y, const real_1d_array &w, const real_2d_array &fmatrix, const ae_int_t n, const ae_int_t m, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinearw(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), n, m, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted linear least squares fitting.
|
|
|
|
QR decomposition is used to reduce task to MxM, then triangular solver or
|
|
SVD-based solver is used depending on condition number of the system. It
|
|
allows to maximize speed and retain decent accuracy.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
W - array[0..N-1] Weights corresponding to function values.
|
|
Each summand in square sum of approximation deviations
|
|
from given values is multiplied by the square of
|
|
corresponding weight.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I, J] - value of J-th basis function in I-th point.
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -1 incorrect N/M were specified
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* Rep.TaskRCond reciprocal of condition number
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearw(const real_1d_array &y, const real_1d_array &w, const real_2d_array &fmatrix, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
if( (y.length()!=w.length()) || (y.length()!=fmatrix.rows()))
|
|
throw ap_error("Error while calling 'lsfitlinearw': looks like one of arguments has wrong size");
|
|
n = y.length();
|
|
m = fmatrix.cols();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinearw(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), n, m, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted constained linear least squares fitting.
|
|
|
|
This is variation of LSFitLinearW(), which searchs for min|A*x=b| given
|
|
that K additional constaints C*x=bc are satisfied. It reduces original
|
|
task to modified one: min|B*y-d| WITHOUT constraints, then LSFitLinearW()
|
|
is called.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
W - array[0..N-1] Weights corresponding to function values.
|
|
Each summand in square sum of approximation deviations
|
|
from given values is multiplied by the square of
|
|
corresponding weight.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I,J] - value of J-th basis function in I-th point.
|
|
CMatrix - a table of constaints, array[0..K-1,0..M].
|
|
I-th row of CMatrix corresponds to I-th linear constraint:
|
|
CMatrix[I,0]*C[0] + ... + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
K - number of constraints, 0 <= K < M
|
|
K=0 corresponds to absence of constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -3 either too many constraints (M or more),
|
|
degenerate constraints (some constraints are
|
|
repetead twice) or inconsistent constraints were
|
|
specified.
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearwc(const real_1d_array &y, const real_1d_array &w, const real_2d_array &fmatrix, const real_2d_array &cmatrix, const ae_int_t n, const ae_int_t m, const ae_int_t k, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinearwc(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), const_cast<alglib_impl::ae_matrix*>(cmatrix.c_ptr()), n, m, k, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted constained linear least squares fitting.
|
|
|
|
This is variation of LSFitLinearW(), which searchs for min|A*x=b| given
|
|
that K additional constaints C*x=bc are satisfied. It reduces original
|
|
task to modified one: min|B*y-d| WITHOUT constraints, then LSFitLinearW()
|
|
is called.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
W - array[0..N-1] Weights corresponding to function values.
|
|
Each summand in square sum of approximation deviations
|
|
from given values is multiplied by the square of
|
|
corresponding weight.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I,J] - value of J-th basis function in I-th point.
|
|
CMatrix - a table of constaints, array[0..K-1,0..M].
|
|
I-th row of CMatrix corresponds to I-th linear constraint:
|
|
CMatrix[I,0]*C[0] + ... + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
K - number of constraints, 0 <= K < M
|
|
K=0 corresponds to absence of constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -3 either too many constraints (M or more),
|
|
degenerate constraints (some constraints are
|
|
repetead twice) or inconsistent constraints were
|
|
specified.
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearwc(const real_1d_array &y, const real_1d_array &w, const real_2d_array &fmatrix, const real_2d_array &cmatrix, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (y.length()!=w.length()) || (y.length()!=fmatrix.rows()))
|
|
throw ap_error("Error while calling 'lsfitlinearwc': looks like one of arguments has wrong size");
|
|
if( (fmatrix.cols()!=cmatrix.cols()-1))
|
|
throw ap_error("Error while calling 'lsfitlinearwc': looks like one of arguments has wrong size");
|
|
n = y.length();
|
|
m = fmatrix.cols();
|
|
k = cmatrix.rows();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinearwc(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), const_cast<alglib_impl::ae_matrix*>(cmatrix.c_ptr()), n, m, k, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Linear least squares fitting.
|
|
|
|
QR decomposition is used to reduce task to MxM, then triangular solver or
|
|
SVD-based solver is used depending on condition number of the system. It
|
|
allows to maximize speed and retain decent accuracy.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I, J] - value of J-th basis function in I-th point.
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* Rep.TaskRCond reciprocal of condition number
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinear(const real_1d_array &y, const real_2d_array &fmatrix, const ae_int_t n, const ae_int_t m, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinear(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), n, m, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Linear least squares fitting.
|
|
|
|
QR decomposition is used to reduce task to MxM, then triangular solver or
|
|
SVD-based solver is used depending on condition number of the system. It
|
|
allows to maximize speed and retain decent accuracy.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I, J] - value of J-th basis function in I-th point.
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* Rep.TaskRCond reciprocal of condition number
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinear(const real_1d_array &y, const real_2d_array &fmatrix, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
if( (y.length()!=fmatrix.rows()))
|
|
throw ap_error("Error while calling 'lsfitlinear': looks like one of arguments has wrong size");
|
|
n = y.length();
|
|
m = fmatrix.cols();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinear(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), n, m, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Constained linear least squares fitting.
|
|
|
|
This is variation of LSFitLinear(), which searchs for min|A*x=b| given
|
|
that K additional constaints C*x=bc are satisfied. It reduces original
|
|
task to modified one: min|B*y-d| WITHOUT constraints, then LSFitLinear()
|
|
is called.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I,J] - value of J-th basis function in I-th point.
|
|
CMatrix - a table of constaints, array[0..K-1,0..M].
|
|
I-th row of CMatrix corresponds to I-th linear constraint:
|
|
CMatrix[I,0]*C[0] + ... + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
K - number of constraints, 0 <= K < M
|
|
K=0 corresponds to absence of constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -3 either too many constraints (M or more),
|
|
degenerate constraints (some constraints are
|
|
repetead twice) or inconsistent constraints were
|
|
specified.
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearc(const real_1d_array &y, const real_2d_array &fmatrix, const real_2d_array &cmatrix, const ae_int_t n, const ae_int_t m, const ae_int_t k, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinearc(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), const_cast<alglib_impl::ae_matrix*>(cmatrix.c_ptr()), n, m, k, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Constained linear least squares fitting.
|
|
|
|
This is variation of LSFitLinear(), which searchs for min|A*x=b| given
|
|
that K additional constaints C*x=bc are satisfied. It reduces original
|
|
task to modified one: min|B*y-d| WITHOUT constraints, then LSFitLinear()
|
|
is called.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I,J] - value of J-th basis function in I-th point.
|
|
CMatrix - a table of constaints, array[0..K-1,0..M].
|
|
I-th row of CMatrix corresponds to I-th linear constraint:
|
|
CMatrix[I,0]*C[0] + ... + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
K - number of constraints, 0 <= K < M
|
|
K=0 corresponds to absence of constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -3 either too many constraints (M or more),
|
|
degenerate constraints (some constraints are
|
|
repetead twice) or inconsistent constraints were
|
|
specified.
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearc(const real_1d_array &y, const real_2d_array &fmatrix, const real_2d_array &cmatrix, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (y.length()!=fmatrix.rows()))
|
|
throw ap_error("Error while calling 'lsfitlinearc': looks like one of arguments has wrong size");
|
|
if( (fmatrix.cols()!=cmatrix.cols()-1))
|
|
throw ap_error("Error while calling 'lsfitlinearc': looks like one of arguments has wrong size");
|
|
n = y.length();
|
|
m = fmatrix.cols();
|
|
k = cmatrix.rows();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitlinearc(const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_matrix*>(fmatrix.c_ptr()), const_cast<alglib_impl::ae_matrix*>(cmatrix.c_ptr()), n, m, k, &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using function values only.
|
|
|
|
Combination of numerical differentiation and secant updates is used to
|
|
obtain function Jacobian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]).
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
DiffStep- numerical differentiation step;
|
|
should not be very small or large;
|
|
large = loss of accuracy
|
|
small = growth of round-off errors
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 18.10.2008 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewf(const real_2d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &c, const ae_int_t n, const ae_int_t m, const ae_int_t k, const double diffstep, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatewf(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, diffstep, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using function values only.
|
|
|
|
Combination of numerical differentiation and secant updates is used to
|
|
obtain function Jacobian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]).
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
DiffStep- numerical differentiation step;
|
|
should not be very small or large;
|
|
large = loss of accuracy
|
|
small = growth of round-off errors
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 18.10.2008 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewf(const real_2d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &c, const double diffstep, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()) || (x.rows()!=w.length()))
|
|
throw ap_error("Error while calling 'lsfitcreatewf': looks like one of arguments has wrong size");
|
|
n = x.rows();
|
|
m = x.cols();
|
|
k = c.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatewf(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, diffstep, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using function values only.
|
|
|
|
Combination of numerical differentiation and secant updates is used to
|
|
obtain function Jacobian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (f(c,x[0])-y[0])^2 + ... + (f(c,x[n-1])-y[n-1])^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]).
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
DiffStep- numerical differentiation step;
|
|
should not be very small or large;
|
|
large = loss of accuracy
|
|
small = growth of round-off errors
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 18.10.2008 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatef(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, const ae_int_t n, const ae_int_t m, const ae_int_t k, const double diffstep, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatef(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, diffstep, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using function values only.
|
|
|
|
Combination of numerical differentiation and secant updates is used to
|
|
obtain function Jacobian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (f(c,x[0])-y[0])^2 + ... + (f(c,x[n-1])-y[n-1])^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]).
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
DiffStep- numerical differentiation step;
|
|
should not be very small or large;
|
|
large = loss of accuracy
|
|
small = growth of round-off errors
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 18.10.2008 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatef(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, const double diffstep, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()))
|
|
throw ap_error("Error while calling 'lsfitcreatef': looks like one of arguments has wrong size");
|
|
n = x.rows();
|
|
m = x.cols();
|
|
k = c.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatef(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, diffstep, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using gradient only.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]) and its gradient.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
CheapFG - boolean flag, which is:
|
|
* True if both function and gradient calculation complexity
|
|
are less than O(M^2). An improved algorithm can
|
|
be used which corresponds to FGJ scheme from
|
|
MINLM unit.
|
|
* False otherwise.
|
|
Standard Jacibian-bases Levenberg-Marquardt algo
|
|
will be used (FJ scheme).
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
See also:
|
|
LSFitResults
|
|
LSFitCreateFG (fitting without weights)
|
|
LSFitCreateWFGH (fitting using Hessian)
|
|
LSFitCreateFGH (fitting using Hessian, without weights)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewfg(const real_2d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &c, const ae_int_t n, const ae_int_t m, const ae_int_t k, const bool cheapfg, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatewfg(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, cheapfg, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using gradient only.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]) and its gradient.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
CheapFG - boolean flag, which is:
|
|
* True if both function and gradient calculation complexity
|
|
are less than O(M^2). An improved algorithm can
|
|
be used which corresponds to FGJ scheme from
|
|
MINLM unit.
|
|
* False otherwise.
|
|
Standard Jacibian-bases Levenberg-Marquardt algo
|
|
will be used (FJ scheme).
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
See also:
|
|
LSFitResults
|
|
LSFitCreateFG (fitting without weights)
|
|
LSFitCreateWFGH (fitting using Hessian)
|
|
LSFitCreateFGH (fitting using Hessian, without weights)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewfg(const real_2d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &c, const bool cheapfg, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()) || (x.rows()!=w.length()))
|
|
throw ap_error("Error while calling 'lsfitcreatewfg': looks like one of arguments has wrong size");
|
|
n = x.rows();
|
|
m = x.cols();
|
|
k = c.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatewfg(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, cheapfg, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using gradient only, without individual
|
|
weights.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]) and its gradient.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
CheapFG - boolean flag, which is:
|
|
* True if both function and gradient calculation complexity
|
|
are less than O(M^2). An improved algorithm can
|
|
be used which corresponds to FGJ scheme from
|
|
MINLM unit.
|
|
* False otherwise.
|
|
Standard Jacibian-bases Levenberg-Marquardt algo
|
|
will be used (FJ scheme).
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatefg(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, const ae_int_t n, const ae_int_t m, const ae_int_t k, const bool cheapfg, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatefg(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, cheapfg, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using gradient only, without individual
|
|
weights.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]) and its gradient.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
CheapFG - boolean flag, which is:
|
|
* True if both function and gradient calculation complexity
|
|
are less than O(M^2). An improved algorithm can
|
|
be used which corresponds to FGJ scheme from
|
|
MINLM unit.
|
|
* False otherwise.
|
|
Standard Jacibian-bases Levenberg-Marquardt algo
|
|
will be used (FJ scheme).
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatefg(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, const bool cheapfg, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()))
|
|
throw ap_error("Error while calling 'lsfitcreatefg': looks like one of arguments has wrong size");
|
|
n = x.rows();
|
|
m = x.cols();
|
|
k = c.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatefg(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, cheapfg, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using gradient/Hessian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses f(c,x[i]), its gradient and its Hessian.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewfgh(const real_2d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &c, const ae_int_t n, const ae_int_t m, const ae_int_t k, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatewfgh(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using gradient/Hessian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses f(c,x[i]), its gradient and its Hessian.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewfgh(const real_2d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &c, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()) || (x.rows()!=w.length()))
|
|
throw ap_error("Error while calling 'lsfitcreatewfgh': looks like one of arguments has wrong size");
|
|
n = x.rows();
|
|
m = x.cols();
|
|
k = c.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatewfgh(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(w.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using gradient/Hessian, without individial
|
|
weights.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses f(c,x[i]), its gradient and its Hessian.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatefgh(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, const ae_int_t n, const ae_int_t m, const ae_int_t k, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatefgh(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using gradient/Hessian, without individial
|
|
weights.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses f(c,x[i]), its gradient and its Hessian.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatefgh(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()))
|
|
throw ap_error("Error while calling 'lsfitcreatefgh': looks like one of arguments has wrong size");
|
|
n = x.rows();
|
|
m = x.cols();
|
|
k = c.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitcreatefgh(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_vector*>(c.c_ptr()), n, m, k, const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Stopping conditions for nonlinear least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
EpsF - stopping criterion. Algorithm stops if
|
|
|F(k+1)-F(k)| <= EpsF*max{|F(k)|, |F(k+1)|, 1}
|
|
EpsX - >=0
|
|
The subroutine finishes its work if on k+1-th iteration
|
|
the condition |v|<=EpsX is fulfilled, where:
|
|
* |.| means Euclidian norm
|
|
* v - scaled step vector, v[i]=dx[i]/s[i]
|
|
* dx - ste pvector, dx=X(k+1)-X(k)
|
|
* s - scaling coefficients set by LSFitSetScale()
|
|
MaxIts - maximum number of iterations. If MaxIts=0, the number of
|
|
iterations is unlimited. Only Levenberg-Marquardt
|
|
iterations are counted (L-BFGS/CG iterations are NOT
|
|
counted because their cost is very low compared to that of
|
|
LM).
|
|
|
|
NOTE
|
|
|
|
Passing EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic
|
|
stopping criterion selection (according to the scheme used by MINLM unit).
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetcond(const lsfitstate &state, const double epsf, const double epsx, const ae_int_t maxits)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitsetcond(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), epsf, epsx, maxits, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets maximum step length
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't
|
|
want to limit step length.
|
|
|
|
Use this subroutine when you optimize target function which contains exp()
|
|
or other fast growing functions, and optimization algorithm makes too
|
|
large steps which leads to overflow. This function allows us to reject
|
|
steps that are too large (and therefore expose us to the possible
|
|
overflow) without actually calculating function value at the x+stp*d.
|
|
|
|
NOTE: non-zero StpMax leads to moderate performance degradation because
|
|
intermediate step of preconditioned L-BFGS optimization is incompatible
|
|
with limits on step size.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.04.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetstpmax(const lsfitstate &state, const double stpmax)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitsetstpmax(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), stpmax, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function turns on/off reporting.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
NeedXRep- whether iteration reports are needed or not
|
|
|
|
When reports are needed, State.C (current parameters) and State.F (current
|
|
value of fitting function) are reported.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 15.08.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetxrep(const lsfitstate &state, const bool needxrep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitsetxrep(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), needxrep, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets scaling coefficients for underlying optimizer.
|
|
|
|
ALGLIB optimizers use scaling matrices to test stopping conditions (step
|
|
size and gradient are scaled before comparison with tolerances). Scale of
|
|
the I-th variable is a translation invariant measure of:
|
|
a) "how large" the variable is
|
|
b) how large the step should be to make significant changes in the function
|
|
|
|
Generally, scale is NOT considered to be a form of preconditioner. But LM
|
|
optimizer is unique in that it uses scaling matrix both in the stopping
|
|
condition tests and as Marquardt damping factor.
|
|
|
|
Proper scaling is very important for the algorithm performance. It is less
|
|
important for the quality of results, but still has some influence (it is
|
|
easier to converge when variables are properly scaled, so premature
|
|
stopping is possible when very badly scalled variables are combined with
|
|
relaxed stopping conditions).
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure stores algorithm state
|
|
S - array[N], non-zero scaling coefficients
|
|
S[i] may be negative, sign doesn't matter.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.01.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetscale(const lsfitstate &state, const real_1d_array &s)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitsetscale(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), const_cast<alglib_impl::ae_vector*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets boundary constraints for underlying optimizer
|
|
|
|
Boundary constraints are inactive by default (after initial creation).
|
|
They are preserved until explicitly turned off with another SetBC() call.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure stores algorithm state
|
|
BndL - lower bounds, array[K].
|
|
If some (all) variables are unbounded, you may specify
|
|
very small number or -INF (latter is recommended because
|
|
it will allow solver to use better algorithm).
|
|
BndU - upper bounds, array[K].
|
|
If some (all) variables are unbounded, you may specify
|
|
very large number or +INF (latter is recommended because
|
|
it will allow solver to use better algorithm).
|
|
|
|
NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
|
|
variable will be "frozen" at X[i]=BndL[i]=BndU[i].
|
|
|
|
NOTE 2: unlike other constrained optimization algorithms, this solver has
|
|
following useful properties:
|
|
* bound constraints are always satisfied exactly
|
|
* function is evaluated only INSIDE area specified by bound constraints
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.01.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetbc(const lsfitstate &state, const real_1d_array &bndl, const real_1d_array &bndu)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitsetbc(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), const_cast<alglib_impl::ae_vector*>(bndl.c_ptr()), const_cast<alglib_impl::ae_vector*>(bndu.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function provides reverse communication interface
|
|
Reverse communication interface is not documented or recommended to use.
|
|
See below for functions which provide better documented API
|
|
*************************************************************************/
|
|
bool lsfititeration(const lsfitstate &state)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
ae_bool result = alglib_impl::lsfititeration(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<bool*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
|
|
void lsfitfit(lsfitstate &state,
|
|
void (*func)(const real_1d_array &c, const real_1d_array &x, double &func, void *ptr),
|
|
void (*rep)(const real_1d_array &c, double func, void *ptr),
|
|
void *ptr)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
if( func==NULL )
|
|
throw ap_error("ALGLIB: error in 'lsfitfit()' (func is NULL)");
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
while( alglib_impl::lsfititeration(state.c_ptr(), &_alglib_env_state) )
|
|
{
|
|
if( state.needf )
|
|
{
|
|
func(state.c, state.x, state.f, ptr);
|
|
continue;
|
|
}
|
|
if( state.xupdated )
|
|
{
|
|
if( rep!=NULL )
|
|
rep(state.c, state.f, ptr);
|
|
continue;
|
|
}
|
|
throw ap_error("ALGLIB: error in 'lsfitfit' (some derivatives were not provided?)");
|
|
}
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
|
|
void lsfitfit(lsfitstate &state,
|
|
void (*func)(const real_1d_array &c, const real_1d_array &x, double &func, void *ptr),
|
|
void (*grad)(const real_1d_array &c, const real_1d_array &x, double &func, real_1d_array &grad, void *ptr),
|
|
void (*rep)(const real_1d_array &c, double func, void *ptr),
|
|
void *ptr)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
if( func==NULL )
|
|
throw ap_error("ALGLIB: error in 'lsfitfit()' (func is NULL)");
|
|
if( grad==NULL )
|
|
throw ap_error("ALGLIB: error in 'lsfitfit()' (grad is NULL)");
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
while( alglib_impl::lsfititeration(state.c_ptr(), &_alglib_env_state) )
|
|
{
|
|
if( state.needf )
|
|
{
|
|
func(state.c, state.x, state.f, ptr);
|
|
continue;
|
|
}
|
|
if( state.needfg )
|
|
{
|
|
grad(state.c, state.x, state.f, state.g, ptr);
|
|
continue;
|
|
}
|
|
if( state.xupdated )
|
|
{
|
|
if( rep!=NULL )
|
|
rep(state.c, state.f, ptr);
|
|
continue;
|
|
}
|
|
throw ap_error("ALGLIB: error in 'lsfitfit' (some derivatives were not provided?)");
|
|
}
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
|
|
void lsfitfit(lsfitstate &state,
|
|
void (*func)(const real_1d_array &c, const real_1d_array &x, double &func, void *ptr),
|
|
void (*grad)(const real_1d_array &c, const real_1d_array &x, double &func, real_1d_array &grad, void *ptr),
|
|
void (*hess)(const real_1d_array &c, const real_1d_array &x, double &func, real_1d_array &grad, real_2d_array &hess, void *ptr),
|
|
void (*rep)(const real_1d_array &c, double func, void *ptr),
|
|
void *ptr)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
if( func==NULL )
|
|
throw ap_error("ALGLIB: error in 'lsfitfit()' (func is NULL)");
|
|
if( grad==NULL )
|
|
throw ap_error("ALGLIB: error in 'lsfitfit()' (grad is NULL)");
|
|
if( hess==NULL )
|
|
throw ap_error("ALGLIB: error in 'lsfitfit()' (hess is NULL)");
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
while( alglib_impl::lsfititeration(state.c_ptr(), &_alglib_env_state) )
|
|
{
|
|
if( state.needf )
|
|
{
|
|
func(state.c, state.x, state.f, ptr);
|
|
continue;
|
|
}
|
|
if( state.needfg )
|
|
{
|
|
grad(state.c, state.x, state.f, state.g, ptr);
|
|
continue;
|
|
}
|
|
if( state.needfgh )
|
|
{
|
|
hess(state.c, state.x, state.f, state.g, state.h, ptr);
|
|
continue;
|
|
}
|
|
if( state.xupdated )
|
|
{
|
|
if( rep!=NULL )
|
|
rep(state.c, state.f, ptr);
|
|
continue;
|
|
}
|
|
throw ap_error("ALGLIB: error in 'lsfitfit' (some derivatives were not provided?)");
|
|
}
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting results.
|
|
|
|
Called after return from LSFitFit().
|
|
|
|
INPUT PARAMETERS:
|
|
State - algorithm state
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - completion code:
|
|
* -7 gradient verification failed.
|
|
See LSFitSetGradientCheck() for more information.
|
|
* 1 relative function improvement is no more than
|
|
EpsF.
|
|
* 2 relative step is no more than EpsX.
|
|
* 4 gradient norm is no more than EpsG
|
|
* 5 MaxIts steps was taken
|
|
* 7 stopping conditions are too stringent,
|
|
further improvement is impossible
|
|
C - array[0..K-1], solution
|
|
Rep - optimization report. On success following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
* WRMSError weighted rms error on the (X,Y).
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(J*CovPar*J')),
|
|
where J is Jacobian matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitresults(const lsfitstate &state, ae_int_t &info, real_1d_array &c, lsfitreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitresults(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), &info, const_cast<alglib_impl::ae_vector*>(c.c_ptr()), const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine turns on verification of the user-supplied analytic
|
|
gradient:
|
|
* user calls this subroutine before fitting begins
|
|
* LSFitFit() is called
|
|
* prior to actual fitting, for each point in data set X_i and each
|
|
component of parameters being fited C_j algorithm performs following
|
|
steps:
|
|
* two trial steps are made to C_j-TestStep*S[j] and C_j+TestStep*S[j],
|
|
where C_j is j-th parameter and S[j] is a scale of j-th parameter
|
|
* if needed, steps are bounded with respect to constraints on C[]
|
|
* F(X_i|C) is evaluated at these trial points
|
|
* we perform one more evaluation in the middle point of the interval
|
|
* we build cubic model using function values and derivatives at trial
|
|
points and we compare its prediction with actual value in the middle
|
|
point
|
|
* in case difference between prediction and actual value is higher than
|
|
some predetermined threshold, algorithm stops with completion code -7;
|
|
Rep.VarIdx is set to index of the parameter with incorrect derivative.
|
|
* after verification is over, algorithm proceeds to the actual optimization.
|
|
|
|
NOTE 1: verification needs N*K (points count * parameters count) gradient
|
|
evaluations. It is very costly and you should use it only for low
|
|
dimensional problems, when you want to be sure that you've
|
|
correctly calculated analytic derivatives. You should not use it
|
|
in the production code (unless you want to check derivatives
|
|
provided by some third party).
|
|
|
|
NOTE 2: you should carefully choose TestStep. Value which is too large
|
|
(so large that function behaviour is significantly non-cubic) will
|
|
lead to false alarms. You may use different step for different
|
|
parameters by means of setting scale with LSFitSetScale().
|
|
|
|
NOTE 3: this function may lead to false positives. In case it reports that
|
|
I-th derivative was calculated incorrectly, you may decrease test
|
|
step and try one more time - maybe your function changes too
|
|
sharply and your step is too large for such rapidly chanding
|
|
function.
|
|
|
|
NOTE 4: this function works only for optimizers created with LSFitCreateWFG()
|
|
or LSFitCreateFG() constructors.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure used to store algorithm state
|
|
TestStep - verification step:
|
|
* TestStep=0 turns verification off
|
|
* TestStep>0 activates verification
|
|
|
|
-- ALGLIB --
|
|
Copyright 15.06.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetgradientcheck(const lsfitstate &state, const double teststep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::lsfitsetgradientcheck(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), teststep, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Parametric spline inteprolant: 2-dimensional curve.
|
|
|
|
You should not try to access its members directly - use PSpline2XXXXXXXX()
|
|
functions instead.
|
|
*************************************************************************/
|
|
_pspline2interpolant_owner::_pspline2interpolant_owner()
|
|
{
|
|
p_struct = (alglib_impl::pspline2interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline2interpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_pspline2interpolant_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_pspline2interpolant_owner::_pspline2interpolant_owner(const _pspline2interpolant_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::pspline2interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline2interpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_pspline2interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline2interpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_pspline2interpolant_owner& _pspline2interpolant_owner::operator=(const _pspline2interpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_pspline2interpolant_clear(p_struct);
|
|
if( !alglib_impl::_pspline2interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline2interpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_pspline2interpolant_owner::~_pspline2interpolant_owner()
|
|
{
|
|
alglib_impl::_pspline2interpolant_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::pspline2interpolant* _pspline2interpolant_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::pspline2interpolant* _pspline2interpolant_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::pspline2interpolant*>(p_struct);
|
|
}
|
|
pspline2interpolant::pspline2interpolant() : _pspline2interpolant_owner()
|
|
{
|
|
}
|
|
|
|
pspline2interpolant::pspline2interpolant(const pspline2interpolant &rhs):_pspline2interpolant_owner(rhs)
|
|
{
|
|
}
|
|
|
|
pspline2interpolant& pspline2interpolant::operator=(const pspline2interpolant &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_pspline2interpolant_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
pspline2interpolant::~pspline2interpolant()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Parametric spline inteprolant: 3-dimensional curve.
|
|
|
|
You should not try to access its members directly - use PSpline3XXXXXXXX()
|
|
functions instead.
|
|
*************************************************************************/
|
|
_pspline3interpolant_owner::_pspline3interpolant_owner()
|
|
{
|
|
p_struct = (alglib_impl::pspline3interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline3interpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_pspline3interpolant_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_pspline3interpolant_owner::_pspline3interpolant_owner(const _pspline3interpolant_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::pspline3interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline3interpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_pspline3interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline3interpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_pspline3interpolant_owner& _pspline3interpolant_owner::operator=(const _pspline3interpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_pspline3interpolant_clear(p_struct);
|
|
if( !alglib_impl::_pspline3interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline3interpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_pspline3interpolant_owner::~_pspline3interpolant_owner()
|
|
{
|
|
alglib_impl::_pspline3interpolant_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::pspline3interpolant* _pspline3interpolant_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::pspline3interpolant* _pspline3interpolant_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::pspline3interpolant*>(p_struct);
|
|
}
|
|
pspline3interpolant::pspline3interpolant() : _pspline3interpolant_owner()
|
|
{
|
|
}
|
|
|
|
pspline3interpolant::pspline3interpolant(const pspline3interpolant &rhs):_pspline3interpolant_owner(rhs)
|
|
{
|
|
}
|
|
|
|
pspline3interpolant& pspline3interpolant::operator=(const pspline3interpolant &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_pspline3interpolant_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
pspline3interpolant::~pspline3interpolant()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function builds non-periodic 2-dimensional parametric spline which
|
|
starts at (X[0],Y[0]) and ends at (X[N-1],Y[N-1]).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - points, array[0..N-1,0..1].
|
|
XY[I,0:1] corresponds to the Ith point.
|
|
Order of points is important!
|
|
N - points count, N>=5 for Akima splines, N>=2 for other types of
|
|
splines.
|
|
ST - spline type:
|
|
* 0 Akima spline
|
|
* 1 parabolically terminated Catmull-Rom spline (Tension=0)
|
|
* 2 parabolically terminated cubic spline
|
|
PT - parameterization type:
|
|
* 0 uniform
|
|
* 1 chord length
|
|
* 2 centripetal
|
|
|
|
OUTPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
|
|
|
|
NOTES:
|
|
* this function assumes that there all consequent points are distinct.
|
|
I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2), (x2,y2)<>(x3,y3) and so on.
|
|
However, non-consequent points may coincide, i.e. we can have (x0,y0)=
|
|
=(x2,y2).
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2build(const real_2d_array &xy, const ae_int_t n, const ae_int_t st, const ae_int_t pt, pspline2interpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline2build(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, st, pt, const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function builds non-periodic 3-dimensional parametric spline which
|
|
starts at (X[0],Y[0],Z[0]) and ends at (X[N-1],Y[N-1],Z[N-1]).
|
|
|
|
Same as PSpline2Build() function, but for 3D, so we won't duplicate its
|
|
description here.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3build(const real_2d_array &xy, const ae_int_t n, const ae_int_t st, const ae_int_t pt, pspline3interpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline3build(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, st, pt, const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function builds periodic 2-dimensional parametric spline which
|
|
starts at (X[0],Y[0]), goes through all points to (X[N-1],Y[N-1]) and then
|
|
back to (X[0],Y[0]).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - points, array[0..N-1,0..1].
|
|
XY[I,0:1] corresponds to the Ith point.
|
|
XY[N-1,0:1] must be different from XY[0,0:1].
|
|
Order of points is important!
|
|
N - points count, N>=3 for other types of splines.
|
|
ST - spline type:
|
|
* 1 Catmull-Rom spline (Tension=0) with cyclic boundary conditions
|
|
* 2 cubic spline with cyclic boundary conditions
|
|
PT - parameterization type:
|
|
* 0 uniform
|
|
* 1 chord length
|
|
* 2 centripetal
|
|
|
|
OUTPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
|
|
|
|
NOTES:
|
|
* this function assumes that there all consequent points are distinct.
|
|
I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2), (x2,y2)<>(x3,y3) and so on.
|
|
However, non-consequent points may coincide, i.e. we can have (x0,y0)=
|
|
=(x2,y2).
|
|
* last point of sequence is NOT equal to the first point. You shouldn't
|
|
make curve "explicitly periodic" by making them equal.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2buildperiodic(const real_2d_array &xy, const ae_int_t n, const ae_int_t st, const ae_int_t pt, pspline2interpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline2buildperiodic(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, st, pt, const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function builds periodic 3-dimensional parametric spline which
|
|
starts at (X[0],Y[0],Z[0]), goes through all points to (X[N-1],Y[N-1],Z[N-1])
|
|
and then back to (X[0],Y[0],Z[0]).
|
|
|
|
Same as PSpline2Build() function, but for 3D, so we won't duplicate its
|
|
description here.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3buildperiodic(const real_2d_array &xy, const ae_int_t n, const ae_int_t st, const ae_int_t pt, pspline3interpolant &p)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline3buildperiodic(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, st, pt, const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function returns vector of parameter values correspoding to points.
|
|
|
|
I.e. for P created from (X[0],Y[0])...(X[N-1],Y[N-1]) and U=TValues(P) we
|
|
have
|
|
(X[0],Y[0]) = PSpline2Calc(P,U[0]),
|
|
(X[1],Y[1]) = PSpline2Calc(P,U[1]),
|
|
(X[2],Y[2]) = PSpline2Calc(P,U[2]),
|
|
...
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
|
|
OUTPUT PARAMETERS:
|
|
N - array size
|
|
T - array[0..N-1]
|
|
|
|
|
|
NOTES:
|
|
* for non-periodic splines U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]=1
|
|
* for periodic splines U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]<1
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2parametervalues(const pspline2interpolant &p, ae_int_t &n, real_1d_array &t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline2parametervalues(const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), &n, const_cast<alglib_impl::ae_vector*>(t.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function returns vector of parameter values correspoding to points.
|
|
|
|
Same as PSpline2ParameterValues(), but for 3D.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3parametervalues(const pspline3interpolant &p, ae_int_t &n, real_1d_array &t)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline3parametervalues(const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), &n, const_cast<alglib_impl::ae_vector*>(t.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates the value of the parametric spline for a given
|
|
value of parameter T
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-position
|
|
Y - Y-position
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2calc(const pspline2interpolant &p, const double t, double &x, double &y)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline2calc(const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), t, &x, &y, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates the value of the parametric spline for a given
|
|
value of parameter T.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-position
|
|
Y - Y-position
|
|
Z - Z-position
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3calc(const pspline3interpolant &p, const double t, double &x, double &y, double &z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline3calc(const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), t, &x, &y, &z, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates tangent vector for a given value of parameter T
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-component of tangent vector (normalized)
|
|
Y - Y-component of tangent vector (normalized)
|
|
|
|
NOTE:
|
|
X^2+Y^2 is either 1 (for non-zero tangent vector) or 0.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2tangent(const pspline2interpolant &p, const double t, double &x, double &y)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline2tangent(const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), t, &x, &y, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates tangent vector for a given value of parameter T
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-component of tangent vector (normalized)
|
|
Y - Y-component of tangent vector (normalized)
|
|
Z - Z-component of tangent vector (normalized)
|
|
|
|
NOTE:
|
|
X^2+Y^2+Z^2 is either 1 (for non-zero tangent vector) or 0.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3tangent(const pspline3interpolant &p, const double t, double &x, double &y, double &z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline3tangent(const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), t, &x, &y, &z, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates derivative, i.e. it returns (dX/dT,dY/dT).
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - X-derivative
|
|
Y - Y-value
|
|
DY - Y-derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2diff(const pspline2interpolant &p, const double t, double &x, double &dx, double &y, double &dy)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline2diff(const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), t, &x, &dx, &y, &dy, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates derivative, i.e. it returns (dX/dT,dY/dT,dZ/dT).
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - X-derivative
|
|
Y - Y-value
|
|
DY - Y-derivative
|
|
Z - Z-value
|
|
DZ - Z-derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3diff(const pspline3interpolant &p, const double t, double &x, double &dx, double &y, double &dy, double &z, double &dz)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline3diff(const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), t, &x, &dx, &y, &dy, &z, &dz, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates first and second derivative with respect to T.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - derivative
|
|
D2X - second derivative
|
|
Y - Y-value
|
|
DY - derivative
|
|
D2Y - second derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2diff2(const pspline2interpolant &p, const double t, double &x, double &dx, double &d2x, double &y, double &dy, double &d2y)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline2diff2(const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), t, &x, &dx, &d2x, &y, &dy, &d2y, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates first and second derivative with respect to T.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - derivative
|
|
D2X - second derivative
|
|
Y - Y-value
|
|
DY - derivative
|
|
D2Y - second derivative
|
|
Z - Z-value
|
|
DZ - derivative
|
|
D2Z - second derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3diff2(const pspline3interpolant &p, const double t, double &x, double &dx, double &d2x, double &y, double &dy, double &d2y, double &z, double &dz, double &d2z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::pspline3diff2(const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), t, &x, &dx, &d2x, &y, &dy, &d2y, &z, &dz, &d2z, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates arc length, i.e. length of curve between t=a
|
|
and t=b.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
A,B - parameter values corresponding to arc ends:
|
|
* B>A will result in positive length returned
|
|
* B<A will result in negative length returned
|
|
|
|
RESULT:
|
|
length of arc starting at T=A and ending at T=B.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double pspline2arclength(const pspline2interpolant &p, const double a, const double b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::pspline2arclength(const_cast<alglib_impl::pspline2interpolant*>(p.c_ptr()), a, b, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates arc length, i.e. length of curve between t=a
|
|
and t=b.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
A,B - parameter values corresponding to arc ends:
|
|
* B>A will result in positive length returned
|
|
* B<A will result in negative length returned
|
|
|
|
RESULT:
|
|
length of arc starting at T=A and ending at T=B.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double pspline3arclength(const pspline3interpolant &p, const double a, const double b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::pspline3arclength(const_cast<alglib_impl::pspline3interpolant*>(p.c_ptr()), a, b, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
RBF model.
|
|
|
|
Never try to directly work with fields of this object - always use ALGLIB
|
|
functions to use this object.
|
|
*************************************************************************/
|
|
_rbfmodel_owner::_rbfmodel_owner()
|
|
{
|
|
p_struct = (alglib_impl::rbfmodel*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfmodel), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_rbfmodel_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_rbfmodel_owner::_rbfmodel_owner(const _rbfmodel_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::rbfmodel*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfmodel), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_rbfmodel_init_copy(p_struct, const_cast<alglib_impl::rbfmodel*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_rbfmodel_owner& _rbfmodel_owner::operator=(const _rbfmodel_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_rbfmodel_clear(p_struct);
|
|
if( !alglib_impl::_rbfmodel_init_copy(p_struct, const_cast<alglib_impl::rbfmodel*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_rbfmodel_owner::~_rbfmodel_owner()
|
|
{
|
|
alglib_impl::_rbfmodel_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::rbfmodel* _rbfmodel_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::rbfmodel* _rbfmodel_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::rbfmodel*>(p_struct);
|
|
}
|
|
rbfmodel::rbfmodel() : _rbfmodel_owner()
|
|
{
|
|
}
|
|
|
|
rbfmodel::rbfmodel(const rbfmodel &rhs):_rbfmodel_owner(rhs)
|
|
{
|
|
}
|
|
|
|
rbfmodel& rbfmodel::operator=(const rbfmodel &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_rbfmodel_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
rbfmodel::~rbfmodel()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
RBF solution report:
|
|
* TerminationType - termination type, positive values - success,
|
|
non-positive - failure.
|
|
*************************************************************************/
|
|
_rbfreport_owner::_rbfreport_owner()
|
|
{
|
|
p_struct = (alglib_impl::rbfreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_rbfreport_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_rbfreport_owner::_rbfreport_owner(const _rbfreport_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::rbfreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfreport), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_rbfreport_init_copy(p_struct, const_cast<alglib_impl::rbfreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_rbfreport_owner& _rbfreport_owner::operator=(const _rbfreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_rbfreport_clear(p_struct);
|
|
if( !alglib_impl::_rbfreport_init_copy(p_struct, const_cast<alglib_impl::rbfreport*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_rbfreport_owner::~_rbfreport_owner()
|
|
{
|
|
alglib_impl::_rbfreport_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::rbfreport* _rbfreport_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::rbfreport* _rbfreport_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::rbfreport*>(p_struct);
|
|
}
|
|
rbfreport::rbfreport() : _rbfreport_owner() ,arows(p_struct->arows),acols(p_struct->acols),annz(p_struct->annz),iterationscount(p_struct->iterationscount),nmv(p_struct->nmv),terminationtype(p_struct->terminationtype)
|
|
{
|
|
}
|
|
|
|
rbfreport::rbfreport(const rbfreport &rhs):_rbfreport_owner(rhs) ,arows(p_struct->arows),acols(p_struct->acols),annz(p_struct->annz),iterationscount(p_struct->iterationscount),nmv(p_struct->nmv),terminationtype(p_struct->terminationtype)
|
|
{
|
|
}
|
|
|
|
rbfreport& rbfreport::operator=(const rbfreport &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_rbfreport_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
rbfreport::~rbfreport()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function serializes data structure to string.
|
|
|
|
Important properties of s_out:
|
|
* it contains alphanumeric characters, dots, underscores, minus signs
|
|
* these symbols are grouped into words, which are separated by spaces
|
|
and Windows-style (CR+LF) newlines
|
|
* although serializer uses spaces and CR+LF as separators, you can
|
|
replace any separator character by arbitrary combination of spaces,
|
|
tabs, Windows or Unix newlines. It allows flexible reformatting of
|
|
the string in case you want to include it into text or XML file.
|
|
But you should not insert separators into the middle of the "words"
|
|
nor you should change case of letters.
|
|
* s_out can be freely moved between 32-bit and 64-bit systems, little
|
|
and big endian machines, and so on. You can serialize structure on
|
|
32-bit machine and unserialize it on 64-bit one (or vice versa), or
|
|
serialize it on SPARC and unserialize on x86. You can also
|
|
serialize it in C++ version of ALGLIB and unserialize in C# one,
|
|
and vice versa.
|
|
*************************************************************************/
|
|
void rbfserialize(rbfmodel &obj, std::string &s_out)
|
|
{
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
alglib_impl::ae_int_t ssize;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
try
|
|
{
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_alloc_start(&serializer);
|
|
alglib_impl::rbfalloc(&serializer, obj.c_ptr(), &state);
|
|
ssize = alglib_impl::ae_serializer_get_alloc_size(&serializer);
|
|
s_out.clear();
|
|
s_out.reserve((size_t)(ssize+1));
|
|
alglib_impl::ae_serializer_sstart_str(&serializer, &s_out);
|
|
alglib_impl::rbfserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer);
|
|
if( s_out.length()>(size_t)ssize )
|
|
throw ap_error("ALGLIB: serialization integrity error");
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(state.error_msg);
|
|
}
|
|
}
|
|
/*************************************************************************
|
|
This function unserializes data structure from string.
|
|
*************************************************************************/
|
|
void rbfunserialize(std::string &s_in, rbfmodel &obj)
|
|
{
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
try
|
|
{
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_ustart_str(&serializer, &s_in);
|
|
alglib_impl::rbfunserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function creates RBF model for a scalar (NY=1) or vector (NY>1)
|
|
function in a NX-dimensional space (NX=2 or NX=3).
|
|
|
|
Newly created model is empty. It can be used for interpolation right after
|
|
creation, but it just returns zeros. You have to add points to the model,
|
|
tune interpolation settings, and then call model construction function
|
|
RBFBuildModel() which will update model according to your specification.
|
|
|
|
USAGE:
|
|
1. User creates model with RBFCreate()
|
|
2. User adds dataset with RBFSetPoints() (points do NOT have to be on a
|
|
regular grid)
|
|
3. (OPTIONAL) User chooses polynomial term by calling:
|
|
* RBFLinTerm() to set linear term
|
|
* RBFConstTerm() to set constant term
|
|
* RBFZeroTerm() to set zero term
|
|
By default, linear term is used.
|
|
4. User chooses specific RBF algorithm to use: either QNN (RBFSetAlgoQNN)
|
|
or ML (RBFSetAlgoMultiLayer).
|
|
5. User calls RBFBuildModel() function which rebuilds model according to
|
|
the specification
|
|
6. User may call RBFCalc() to calculate model value at the specified point,
|
|
RBFGridCalc() to calculate model values at the points of the regular
|
|
grid. User may extract model coefficients with RBFUnpack() call.
|
|
|
|
INPUT PARAMETERS:
|
|
NX - dimension of the space, NX=2 or NX=3
|
|
NY - function dimension, NY>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - RBF model (initially equals to zero)
|
|
|
|
NOTE 1: memory requirements. RBF models require amount of memory which is
|
|
proportional to the number of data points. Memory is allocated
|
|
during model construction, but most of this memory is freed after
|
|
model coefficients are calculated.
|
|
|
|
Some approximate estimates for N centers with default settings are
|
|
given below:
|
|
* about 250*N*(sizeof(double)+2*sizeof(int)) bytes of memory is
|
|
needed during model construction stage.
|
|
* about 15*N*sizeof(double) bytes is needed after model is built.
|
|
For example, for N=100000 we may need 0.6 GB of memory to build
|
|
model, but just about 0.012 GB to store it.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcreate(const ae_int_t nx, const ae_int_t ny, rbfmodel &s)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfcreate(nx, ny, const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function adds dataset.
|
|
|
|
This function overrides results of the previous calls, i.e. multiple calls
|
|
of this function will result in only the last set being added.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call.
|
|
XY - points, array[N,NX+NY]. One row corresponds to one point
|
|
in the dataset. First NX elements are coordinates, next
|
|
NY elements are function values. Array may be larger than
|
|
specific, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
|
|
After you've added dataset and (optionally) tuned algorithm settings you
|
|
should call RBFBuildModel() in order to build a model for you.
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetpoints(const rbfmodel &s, const real_2d_array &xy, const ae_int_t n)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetpoints(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function adds dataset.
|
|
|
|
This function overrides results of the previous calls, i.e. multiple calls
|
|
of this function will result in only the last set being added.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call.
|
|
XY - points, array[N,NX+NY]. One row corresponds to one point
|
|
in the dataset. First NX elements are coordinates, next
|
|
NY elements are function values. Array may be larger than
|
|
specific, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
|
|
After you've added dataset and (optionally) tuned algorithm settings you
|
|
should call RBFBuildModel() in order to build a model for you.
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetpoints(const rbfmodel &s, const real_2d_array &xy)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = xy.rows();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetpoints(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called RBF-QNN and it is good for point sets with
|
|
following properties:
|
|
a) all points are distinct
|
|
b) all points are well separated.
|
|
c) points distribution is approximately uniform. There is no "contour
|
|
lines", clusters of points, or other small-scale structures.
|
|
|
|
Algorithm description:
|
|
1) interpolation centers are allocated to data points
|
|
2) interpolation radii are calculated as distances to the nearest centers
|
|
times Q coefficient (where Q is a value from [0.75,1.50]).
|
|
3) after performing (2) radii are transformed in order to avoid situation
|
|
when single outlier has very large radius and influences many points
|
|
across all dataset. Transformation has following form:
|
|
new_r[i] = min(r[i],Z*median(r[]))
|
|
where r[i] is I-th radius, median() is a median radius across entire
|
|
dataset, Z is user-specified value which controls amount of deviation
|
|
from median radius.
|
|
|
|
When (a) is violated, we will be unable to build RBF model. When (b) or
|
|
(c) are violated, model will be built, but interpolation quality will be
|
|
low. See http://www.alglib.net/interpolation/ for more information on this
|
|
subject.
|
|
|
|
This algorithm is used by default.
|
|
|
|
Additional Q parameter controls smoothness properties of the RBF basis:
|
|
* Q<0.75 will give perfectly conditioned basis, but terrible smoothness
|
|
properties (RBF interpolant will have sharp peaks around function values)
|
|
* Q around 1.0 gives good balance between smoothness and condition number
|
|
* Q>1.5 will lead to badly conditioned systems and slow convergence of the
|
|
underlying linear solver (although smoothness will be very good)
|
|
* Q>2.0 will effectively make optimizer useless because it won't converge
|
|
within reasonable amount of iterations. It is possible to set such large
|
|
Q, but it is advised not to do so.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
Q - Q parameter, Q>0, recommended value - 1.0
|
|
Z - Z parameter, Z>0, recommended value - 5.0
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgoqnn(const rbfmodel &s, const double q, const double z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetalgoqnn(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), q, z, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called RBF-QNN and it is good for point sets with
|
|
following properties:
|
|
a) all points are distinct
|
|
b) all points are well separated.
|
|
c) points distribution is approximately uniform. There is no "contour
|
|
lines", clusters of points, or other small-scale structures.
|
|
|
|
Algorithm description:
|
|
1) interpolation centers are allocated to data points
|
|
2) interpolation radii are calculated as distances to the nearest centers
|
|
times Q coefficient (where Q is a value from [0.75,1.50]).
|
|
3) after performing (2) radii are transformed in order to avoid situation
|
|
when single outlier has very large radius and influences many points
|
|
across all dataset. Transformation has following form:
|
|
new_r[i] = min(r[i],Z*median(r[]))
|
|
where r[i] is I-th radius, median() is a median radius across entire
|
|
dataset, Z is user-specified value which controls amount of deviation
|
|
from median radius.
|
|
|
|
When (a) is violated, we will be unable to build RBF model. When (b) or
|
|
(c) are violated, model will be built, but interpolation quality will be
|
|
low. See http://www.alglib.net/interpolation/ for more information on this
|
|
subject.
|
|
|
|
This algorithm is used by default.
|
|
|
|
Additional Q parameter controls smoothness properties of the RBF basis:
|
|
* Q<0.75 will give perfectly conditioned basis, but terrible smoothness
|
|
properties (RBF interpolant will have sharp peaks around function values)
|
|
* Q around 1.0 gives good balance between smoothness and condition number
|
|
* Q>1.5 will lead to badly conditioned systems and slow convergence of the
|
|
underlying linear solver (although smoothness will be very good)
|
|
* Q>2.0 will effectively make optimizer useless because it won't converge
|
|
within reasonable amount of iterations. It is possible to set such large
|
|
Q, but it is advised not to do so.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
Q - Q parameter, Q>0, recommended value - 1.0
|
|
Z - Z parameter, Z>0, recommended value - 5.0
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgoqnn(const rbfmodel &s)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
double q;
|
|
double z;
|
|
|
|
q = 1.0;
|
|
z = 5.0;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetalgoqnn(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), q, z, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called RBF-ML. It builds multilayer RBF model, i.e.
|
|
model with subsequently decreasing radii, which allows us to combine
|
|
smoothness (due to large radii of the first layers) with exactness (due
|
|
to small radii of the last layers) and fast convergence.
|
|
|
|
Internally RBF-ML uses many different means of acceleration, from sparse
|
|
matrices to KD-trees, which results in algorithm whose working time is
|
|
roughly proportional to N*log(N)*Density*RBase^2*NLayers, where N is a
|
|
number of points, Density is an average density if points per unit of the
|
|
interpolation space, RBase is an initial radius, NLayers is a number of
|
|
layers.
|
|
|
|
RBF-ML is good for following kinds of interpolation problems:
|
|
1. "exact" problems (perfect fit) with well separated points
|
|
2. least squares problems with arbitrary distribution of points (algorithm
|
|
gives perfect fit where it is possible, and resorts to least squares
|
|
fit in the hard areas).
|
|
3. noisy problems where we want to apply some controlled amount of
|
|
smoothing.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
RBase - RBase parameter, RBase>0
|
|
NLayers - NLayers parameter, NLayers>0, recommended value to start
|
|
with - about 5.
|
|
LambdaV - regularization value, can be useful when solving problem
|
|
in the least squares sense. Optimal lambda is problem-
|
|
dependent and require trial and error. In our experience,
|
|
good lambda can be as large as 0.1, and you can use 0.001
|
|
as initial guess.
|
|
Default value - 0.01, which is used when LambdaV is not
|
|
given. You can specify zero value, but it is not
|
|
recommended to do so.
|
|
|
|
TUNING ALGORITHM
|
|
|
|
In order to use this algorithm you have to choose three parameters:
|
|
* initial radius RBase
|
|
* number of layers in the model NLayers
|
|
* regularization coefficient LambdaV
|
|
|
|
Initial radius is easy to choose - you can pick any number several times
|
|
larger than the average distance between points. Algorithm won't break
|
|
down if you choose radius which is too large (model construction time will
|
|
increase, but model will be built correctly).
|
|
|
|
Choose such number of layers that RLast=RBase/2^(NLayers-1) (radius used
|
|
by the last layer) will be smaller than the typical distance between
|
|
points. In case model error is too large, you can increase number of
|
|
layers. Having more layers will make model construction and evaluation
|
|
proportionally slower, but it will allow you to have model which precisely
|
|
fits your data. From the other side, if you want to suppress noise, you
|
|
can DECREASE number of layers to make your model less flexible.
|
|
|
|
Regularization coefficient LambdaV controls smoothness of the individual
|
|
models built for each layer. We recommend you to use default value in case
|
|
you don't want to tune this parameter, because having non-zero LambdaV
|
|
accelerates and stabilizes internal iterative algorithm. In case you want
|
|
to suppress noise you can use LambdaV as additional parameter (larger
|
|
value = more smoothness) to tune.
|
|
|
|
TYPICAL ERRORS
|
|
|
|
1. Using initial radius which is too large. Memory requirements of the
|
|
RBF-ML are roughly proportional to N*Density*RBase^2 (where Density is
|
|
an average density of points per unit of the interpolation space). In
|
|
the extreme case of the very large RBase we will need O(N^2) units of
|
|
memory - and many layers in order to decrease radius to some reasonably
|
|
small value.
|
|
|
|
2. Using too small number of layers - RBF models with large radius are not
|
|
flexible enough to reproduce small variations in the target function.
|
|
You need many layers with different radii, from large to small, in
|
|
order to have good model.
|
|
|
|
3. Using initial radius which is too small. You will get model with
|
|
"holes" in the areas which are too far away from interpolation centers.
|
|
However, algorithm will work correctly (and quickly) in this case.
|
|
|
|
4. Using too many layers - you will get too large and too slow model. This
|
|
model will perfectly reproduce your function, but maybe you will be
|
|
able to achieve similar results with less layers (and less memory).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.03.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgomultilayer(const rbfmodel &s, const double rbase, const ae_int_t nlayers, const double lambdav)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetalgomultilayer(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), rbase, nlayers, lambdav, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called RBF-ML. It builds multilayer RBF model, i.e.
|
|
model with subsequently decreasing radii, which allows us to combine
|
|
smoothness (due to large radii of the first layers) with exactness (due
|
|
to small radii of the last layers) and fast convergence.
|
|
|
|
Internally RBF-ML uses many different means of acceleration, from sparse
|
|
matrices to KD-trees, which results in algorithm whose working time is
|
|
roughly proportional to N*log(N)*Density*RBase^2*NLayers, where N is a
|
|
number of points, Density is an average density if points per unit of the
|
|
interpolation space, RBase is an initial radius, NLayers is a number of
|
|
layers.
|
|
|
|
RBF-ML is good for following kinds of interpolation problems:
|
|
1. "exact" problems (perfect fit) with well separated points
|
|
2. least squares problems with arbitrary distribution of points (algorithm
|
|
gives perfect fit where it is possible, and resorts to least squares
|
|
fit in the hard areas).
|
|
3. noisy problems where we want to apply some controlled amount of
|
|
smoothing.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
RBase - RBase parameter, RBase>0
|
|
NLayers - NLayers parameter, NLayers>0, recommended value to start
|
|
with - about 5.
|
|
LambdaV - regularization value, can be useful when solving problem
|
|
in the least squares sense. Optimal lambda is problem-
|
|
dependent and require trial and error. In our experience,
|
|
good lambda can be as large as 0.1, and you can use 0.001
|
|
as initial guess.
|
|
Default value - 0.01, which is used when LambdaV is not
|
|
given. You can specify zero value, but it is not
|
|
recommended to do so.
|
|
|
|
TUNING ALGORITHM
|
|
|
|
In order to use this algorithm you have to choose three parameters:
|
|
* initial radius RBase
|
|
* number of layers in the model NLayers
|
|
* regularization coefficient LambdaV
|
|
|
|
Initial radius is easy to choose - you can pick any number several times
|
|
larger than the average distance between points. Algorithm won't break
|
|
down if you choose radius which is too large (model construction time will
|
|
increase, but model will be built correctly).
|
|
|
|
Choose such number of layers that RLast=RBase/2^(NLayers-1) (radius used
|
|
by the last layer) will be smaller than the typical distance between
|
|
points. In case model error is too large, you can increase number of
|
|
layers. Having more layers will make model construction and evaluation
|
|
proportionally slower, but it will allow you to have model which precisely
|
|
fits your data. From the other side, if you want to suppress noise, you
|
|
can DECREASE number of layers to make your model less flexible.
|
|
|
|
Regularization coefficient LambdaV controls smoothness of the individual
|
|
models built for each layer. We recommend you to use default value in case
|
|
you don't want to tune this parameter, because having non-zero LambdaV
|
|
accelerates and stabilizes internal iterative algorithm. In case you want
|
|
to suppress noise you can use LambdaV as additional parameter (larger
|
|
value = more smoothness) to tune.
|
|
|
|
TYPICAL ERRORS
|
|
|
|
1. Using initial radius which is too large. Memory requirements of the
|
|
RBF-ML are roughly proportional to N*Density*RBase^2 (where Density is
|
|
an average density of points per unit of the interpolation space). In
|
|
the extreme case of the very large RBase we will need O(N^2) units of
|
|
memory - and many layers in order to decrease radius to some reasonably
|
|
small value.
|
|
|
|
2. Using too small number of layers - RBF models with large radius are not
|
|
flexible enough to reproduce small variations in the target function.
|
|
You need many layers with different radii, from large to small, in
|
|
order to have good model.
|
|
|
|
3. Using initial radius which is too small. You will get model with
|
|
"holes" in the areas which are too far away from interpolation centers.
|
|
However, algorithm will work correctly (and quickly) in this case.
|
|
|
|
4. Using too many layers - you will get too large and too slow model. This
|
|
model will perfectly reproduce your function, but maybe you will be
|
|
able to achieve similar results with less layers (and less memory).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.03.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgomultilayer(const rbfmodel &s, const double rbase, const ae_int_t nlayers)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
double lambdav;
|
|
|
|
lambdav = 0.01;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetalgomultilayer(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), rbase, nlayers, lambdav, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets linear term (model is a sum of radial basis functions
|
|
plus linear polynomial). This function won't have effect until next call
|
|
to RBFBuildModel().
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetlinterm(const rbfmodel &s)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetlinterm(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets constant term (model is a sum of radial basis functions
|
|
plus constant). This function won't have effect until next call to
|
|
RBFBuildModel().
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetconstterm(const rbfmodel &s)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetconstterm(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets zero term (model is a sum of radial basis functions
|
|
without polynomial term). This function won't have effect until next call
|
|
to RBFBuildModel().
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetzeroterm(const rbfmodel &s)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfsetzeroterm(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function builds RBF model and returns report (contains some
|
|
information which can be used for evaluation of the algorithm properties).
|
|
|
|
Call to this function modifies RBF model by calculating its centers/radii/
|
|
weights and saving them into RBFModel structure. Initially RBFModel
|
|
contain zero coefficients, but after call to this function we will have
|
|
coefficients which were calculated in order to fit our dataset.
|
|
|
|
After you called this function you can call RBFCalc(), RBFGridCalc() and
|
|
other model calculation functions.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
Rep - report:
|
|
* Rep.TerminationType:
|
|
* -5 - non-distinct basis function centers were detected,
|
|
interpolation aborted
|
|
* -4 - nonconvergence of the internal SVD solver
|
|
* 1 - successful termination
|
|
Fields are used for debugging purposes:
|
|
* Rep.IterationsCount - iterations count of the LSQR solver
|
|
* Rep.NMV - number of matrix-vector products
|
|
* Rep.ARows - rows count for the system matrix
|
|
* Rep.ACols - columns count for the system matrix
|
|
* Rep.ANNZ - number of significantly non-zero elements
|
|
(elements above some algorithm-determined threshold)
|
|
|
|
NOTE: failure to build model will leave current state of the structure
|
|
unchanged.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfbuildmodel(const rbfmodel &s, rbfreport &rep)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfbuildmodel(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::rbfreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model in the given point.
|
|
|
|
This function should be used when we have NY=1 (scalar function) and NX=2
|
|
(2-dimensional space). If you have 3-dimensional space, use RBFCalc3(). If
|
|
you have general situation (NX-dimensional space, NY-dimensional function)
|
|
you should use general, less efficient implementation RBFCalc().
|
|
|
|
If you want to calculate function values many times, consider using
|
|
RBFGridCalc2(), which is far more efficient than many subsequent calls to
|
|
RBFCalc2().
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
*NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - first coordinate, finite number
|
|
X1 - second coordinate, finite number
|
|
|
|
RESULT:
|
|
value of the model or 0.0 (as defined above)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfcalc2(const rbfmodel &s, const double x0, const double x1)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::rbfcalc2(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), x0, x1, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model in the given point.
|
|
|
|
This function should be used when we have NY=1 (scalar function) and NX=3
|
|
(3-dimensional space). If you have 2-dimensional space, use RBFCalc2(). If
|
|
you have general situation (NX-dimensional space, NY-dimensional function)
|
|
you should use general, less efficient implementation RBFCalc().
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>3
|
|
*NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - first coordinate, finite number
|
|
X1 - second coordinate, finite number
|
|
X2 - third coordinate, finite number
|
|
|
|
RESULT:
|
|
value of the model or 0.0 (as defined above)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfcalc3(const rbfmodel &s, const double x0, const double x1, const double x2)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::rbfcalc3(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), x0, x1, x2, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the given point.
|
|
|
|
This is general function which can be used for arbitrary NX (dimension of
|
|
the space of arguments) and NY (dimension of the function itself). However
|
|
when you have NY=1 you may find more convenient to use RBFCalc2() or
|
|
RBFCalc3().
|
|
|
|
This function returns 0.0 when model is not initialized.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X - coordinates, array[NX].
|
|
X may have more than NX elements, in this case only
|
|
leading NX will be used.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function value, array[NY]. Y is out-parameter and
|
|
reallocated after call to this function. In case you want
|
|
to reuse previously allocated Y, you may use RBFCalcBuf(),
|
|
which reallocates Y only when it is too small.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcalc(const rbfmodel &s, const real_1d_array &x, real_1d_array &y)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfcalc(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the given point.
|
|
|
|
Same as RBFCalc(), but does not reallocate Y when in is large enough to
|
|
store function values.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X - coordinates, array[NX].
|
|
X may have more than NX elements, in this case only
|
|
leading NX will be used.
|
|
Y - possibly preallocated array
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function value, array[NY]. Y is not reallocated when it
|
|
is larger than NY.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcalcbuf(const rbfmodel &s, const real_1d_array &x, real_1d_array &y)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfcalcbuf(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the regular grid.
|
|
|
|
Grid have N0*N1 points, with Point[I,J] = (X0[I], X1[J])
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
*NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - array of grid nodes, first coordinates, array[N0]
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[N0,N1]. Y is out-variable and
|
|
is reallocated by this function.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc2(const rbfmodel &s, const real_1d_array &x0, const ae_int_t n0, const real_1d_array &x1, const ae_int_t n1, real_2d_array &y)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfgridcalc2(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::ae_vector*>(x0.c_ptr()), n0, const_cast<alglib_impl::ae_vector*>(x1.c_ptr()), n1, const_cast<alglib_impl::ae_matrix*>(y.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function "unpacks" RBF model by extracting its coefficients.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
|
|
OUTPUT PARAMETERS:
|
|
NX - dimensionality of argument
|
|
NY - dimensionality of the target function
|
|
XWR - model information, array[NC,NX+NY+1].
|
|
One row of the array corresponds to one basis function:
|
|
* first NX columns - coordinates of the center
|
|
* next NY columns - weights, one per dimension of the
|
|
function being modelled
|
|
* last column - radius, same for all dimensions of
|
|
the function being modelled
|
|
NC - number of the centers
|
|
V - polynomial term , array[NY,NX+1]. One row per one
|
|
dimension of the function being modelled. First NX
|
|
elements are linear coefficients, V[NX] is equal to the
|
|
constant part.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfunpack(const rbfmodel &s, ae_int_t &nx, ae_int_t &ny, real_2d_array &xwr, ae_int_t &nc, real_2d_array &v)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::rbfunpack(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &nx, &ny, const_cast<alglib_impl::ae_matrix*>(xwr.c_ptr()), &nc, const_cast<alglib_impl::ae_matrix*>(v.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
2-dimensional spline inteprolant
|
|
*************************************************************************/
|
|
_spline2dinterpolant_owner::_spline2dinterpolant_owner()
|
|
{
|
|
p_struct = (alglib_impl::spline2dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline2dinterpolant_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline2dinterpolant_owner::_spline2dinterpolant_owner(const _spline2dinterpolant_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::spline2dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline2dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline2dinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline2dinterpolant_owner& _spline2dinterpolant_owner::operator=(const _spline2dinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_spline2dinterpolant_clear(p_struct);
|
|
if( !alglib_impl::_spline2dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline2dinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_spline2dinterpolant_owner::~_spline2dinterpolant_owner()
|
|
{
|
|
alglib_impl::_spline2dinterpolant_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::spline2dinterpolant* _spline2dinterpolant_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::spline2dinterpolant* _spline2dinterpolant_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::spline2dinterpolant*>(p_struct);
|
|
}
|
|
spline2dinterpolant::spline2dinterpolant() : _spline2dinterpolant_owner()
|
|
{
|
|
}
|
|
|
|
spline2dinterpolant::spline2dinterpolant(const spline2dinterpolant &rhs):_spline2dinterpolant_owner(rhs)
|
|
{
|
|
}
|
|
|
|
spline2dinterpolant& spline2dinterpolant::operator=(const spline2dinterpolant &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_spline2dinterpolant_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
spline2dinterpolant::~spline2dinterpolant()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the bilinear or bicubic spline at
|
|
the given point X.
|
|
|
|
Input parameters:
|
|
C - coefficients table.
|
|
Built by BuildBilinearSpline or BuildBicubicSpline.
|
|
X, Y- point
|
|
|
|
Result:
|
|
S(x,y)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline2dcalc(const spline2dinterpolant &c, const double x, const double y)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::spline2dcalc(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), x, y, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the bilinear or bicubic spline at
|
|
the given point X and its derivatives.
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
|
|
Output parameters:
|
|
F - S(x,y)
|
|
FX - dS(x,y)/dX
|
|
FY - dS(x,y)/dY
|
|
FXY - d2S(x,y)/dXdY
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2ddiff(const spline2dinterpolant &c, const double x, const double y, double &f, double &fx, double &fy, double &fxy)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2ddiff(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), x, y, &f, &fx, &fy, &fxy, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline argument.
|
|
|
|
Input parameters:
|
|
C - spline interpolant
|
|
AX, BX - transformation coefficients: x = A*t + B
|
|
AY, BY - transformation coefficients: y = A*u + B
|
|
Result:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dlintransxy(const spline2dinterpolant &c, const double ax, const double bx, const double ay, const double by)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dlintransxy(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), ax, bx, ay, by, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline.
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: S2(x,y) = A*S(x,y) + B
|
|
|
|
Output parameters:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dlintransf(const spline2dinterpolant &c, const double a, const double b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dlintransf(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), a, b, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine makes the copy of the spline model.
|
|
|
|
Input parameters:
|
|
C - spline interpolant
|
|
|
|
Output parameters:
|
|
CC - spline copy
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcopy(const spline2dinterpolant &c, spline2dinterpolant &cc)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dcopy(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), const_cast<alglib_impl::spline2dinterpolant*>(cc.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Bicubic spline resampling
|
|
|
|
Input parameters:
|
|
A - function values at the old grid,
|
|
array[0..OldHeight-1, 0..OldWidth-1]
|
|
OldHeight - old grid height, OldHeight>1
|
|
OldWidth - old grid width, OldWidth>1
|
|
NewHeight - new grid height, NewHeight>1
|
|
NewWidth - new grid width, NewWidth>1
|
|
|
|
Output parameters:
|
|
B - function values at the new grid,
|
|
array[0..NewHeight-1, 0..NewWidth-1]
|
|
|
|
-- ALGLIB routine --
|
|
15 May, 2007
|
|
Copyright by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dresamplebicubic(const real_2d_array &a, const ae_int_t oldheight, const ae_int_t oldwidth, real_2d_array &b, const ae_int_t newheight, const ae_int_t newwidth)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dresamplebicubic(const_cast<alglib_impl::ae_matrix*>(a.c_ptr()), oldheight, oldwidth, const_cast<alglib_impl::ae_matrix*>(b.c_ptr()), newheight, newwidth, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Bilinear spline resampling
|
|
|
|
Input parameters:
|
|
A - function values at the old grid,
|
|
array[0..OldHeight-1, 0..OldWidth-1]
|
|
OldHeight - old grid height, OldHeight>1
|
|
OldWidth - old grid width, OldWidth>1
|
|
NewHeight - new grid height, NewHeight>1
|
|
NewWidth - new grid width, NewWidth>1
|
|
|
|
Output parameters:
|
|
B - function values at the new grid,
|
|
array[0..NewHeight-1, 0..NewWidth-1]
|
|
|
|
-- ALGLIB routine --
|
|
09.07.2007
|
|
Copyright by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dresamplebilinear(const real_2d_array &a, const ae_int_t oldheight, const ae_int_t oldwidth, real_2d_array &b, const ae_int_t newheight, const ae_int_t newwidth)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dresamplebilinear(const_cast<alglib_impl::ae_matrix*>(a.c_ptr()), oldheight, oldwidth, const_cast<alglib_impl::ae_matrix*>(b.c_ptr()), newheight, newwidth, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds bilinear vector-valued spline.
|
|
|
|
Input parameters:
|
|
X - spline abscissas, array[0..N-1]
|
|
Y - spline ordinates, array[0..M-1]
|
|
F - function values, array[0..M*N*D-1]:
|
|
* first D elements store D values at (X[0],Y[0])
|
|
* next D elements store D values at (X[1],Y[0])
|
|
* general form - D function values at (X[i],Y[j]) are stored
|
|
at F[D*(J*N+I)...D*(J*N+I)+D-1].
|
|
M,N - grid size, M>=2, N>=2
|
|
D - vector dimension, D>=1
|
|
|
|
Output parameters:
|
|
C - spline interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbilinearv(const real_1d_array &x, const ae_int_t n, const real_1d_array &y, const ae_int_t m, const real_1d_array &f, const ae_int_t d, spline2dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dbuildbilinearv(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), d, const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds bicubic vector-valued spline.
|
|
|
|
Input parameters:
|
|
X - spline abscissas, array[0..N-1]
|
|
Y - spline ordinates, array[0..M-1]
|
|
F - function values, array[0..M*N*D-1]:
|
|
* first D elements store D values at (X[0],Y[0])
|
|
* next D elements store D values at (X[1],Y[0])
|
|
* general form - D function values at (X[i],Y[j]) are stored
|
|
at F[D*(J*N+I)...D*(J*N+I)+D-1].
|
|
M,N - grid size, M>=2, N>=2
|
|
D - vector dimension, D>=1
|
|
|
|
Output parameters:
|
|
C - spline interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbicubicv(const real_1d_array &x, const ae_int_t n, const real_1d_array &y, const ae_int_t m, const real_1d_array &f, const ae_int_t d, spline2dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dbuildbicubicv(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), d, const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
F - output buffer, possibly preallocated array. In case array size
|
|
is large enough to store result, it is not reallocated. Array
|
|
which is too short will be reallocated
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] (or larger) which stores function values
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcalcvbuf(const spline2dinterpolant &c, const double x, const double y, real_1d_array &f)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dcalcvbuf(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), x, y, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] which stores function values. F is out-parameter and
|
|
it is reallocated after call to this function. In case you
|
|
want to reuse previously allocated F, you may use
|
|
Spline2DCalcVBuf(), which reallocates F only when it is too
|
|
small.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcalcv(const spline2dinterpolant &c, const double x, const double y, real_1d_array &f)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dcalcv(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), x, y, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine unpacks two-dimensional spline into the coefficients table
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
|
|
Result:
|
|
M, N- grid size (x-axis and y-axis)
|
|
D - number of components
|
|
Tbl - coefficients table, unpacked format,
|
|
D - components: [0..(N-1)*(M-1)*D-1, 0..19].
|
|
For T=0..D-1 (component index), I = 0...N-2 (x index),
|
|
J=0..M-2 (y index):
|
|
K := T + I*D + J*D*(N-1)
|
|
|
|
K-th row stores decomposition for T-th component of the
|
|
vector-valued function
|
|
|
|
Tbl[K,0] = X[i]
|
|
Tbl[K,1] = X[i+1]
|
|
Tbl[K,2] = Y[j]
|
|
Tbl[K,3] = Y[j+1]
|
|
Tbl[K,4] = C00
|
|
Tbl[K,5] = C01
|
|
Tbl[K,6] = C02
|
|
Tbl[K,7] = C03
|
|
Tbl[K,8] = C10
|
|
Tbl[K,9] = C11
|
|
...
|
|
Tbl[K,19] = C33
|
|
On each grid square spline is equals to:
|
|
S(x) = SUM(c[i,j]*(t^i)*(u^j), i=0..3, j=0..3)
|
|
t = x-x[j]
|
|
u = y-y[i]
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dunpackv(const spline2dinterpolant &c, ae_int_t &m, ae_int_t &n, ae_int_t &d, real_2d_array &tbl)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dunpackv(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), &m, &n, &d, const_cast<alglib_impl::ae_matrix*>(tbl.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine was deprecated in ALGLIB 3.6.0
|
|
|
|
We recommend you to switch to Spline2DBuildBilinearV(), which is more
|
|
flexible and accepts its arguments in more convenient order.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbilinear(const real_1d_array &x, const real_1d_array &y, const real_2d_array &f, const ae_int_t m, const ae_int_t n, spline2dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dbuildbilinear(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_matrix*>(f.c_ptr()), m, n, const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine was deprecated in ALGLIB 3.6.0
|
|
|
|
We recommend you to switch to Spline2DBuildBicubicV(), which is more
|
|
flexible and accepts its arguments in more convenient order.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbicubic(const real_1d_array &x, const real_1d_array &y, const real_2d_array &f, const ae_int_t m, const ae_int_t n, spline2dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dbuildbicubic(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), const_cast<alglib_impl::ae_matrix*>(f.c_ptr()), m, n, const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine was deprecated in ALGLIB 3.6.0
|
|
|
|
We recommend you to switch to Spline2DUnpackV(), which is more flexible
|
|
and accepts its arguments in more convenient order.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dunpack(const spline2dinterpolant &c, ae_int_t &m, ae_int_t &n, real_2d_array &tbl)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline2dunpack(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), &m, &n, const_cast<alglib_impl::ae_matrix*>(tbl.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
3-dimensional spline inteprolant
|
|
*************************************************************************/
|
|
_spline3dinterpolant_owner::_spline3dinterpolant_owner()
|
|
{
|
|
p_struct = (alglib_impl::spline3dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline3dinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline3dinterpolant_init(p_struct, NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline3dinterpolant_owner::_spline3dinterpolant_owner(const _spline3dinterpolant_owner &rhs)
|
|
{
|
|
p_struct = (alglib_impl::spline3dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline3dinterpolant), NULL);
|
|
if( p_struct==NULL )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
if( !alglib_impl::_spline3dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline3dinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
}
|
|
|
|
_spline3dinterpolant_owner& _spline3dinterpolant_owner::operator=(const _spline3dinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
alglib_impl::_spline3dinterpolant_clear(p_struct);
|
|
if( !alglib_impl::_spline3dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline3dinterpolant*>(rhs.p_struct), NULL, ae_false) )
|
|
throw ap_error("ALGLIB: malloc error");
|
|
return *this;
|
|
}
|
|
|
|
_spline3dinterpolant_owner::~_spline3dinterpolant_owner()
|
|
{
|
|
alglib_impl::_spline3dinterpolant_clear(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
|
|
alglib_impl::spline3dinterpolant* _spline3dinterpolant_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::spline3dinterpolant* _spline3dinterpolant_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::spline3dinterpolant*>(p_struct);
|
|
}
|
|
spline3dinterpolant::spline3dinterpolant() : _spline3dinterpolant_owner()
|
|
{
|
|
}
|
|
|
|
spline3dinterpolant::spline3dinterpolant(const spline3dinterpolant &rhs):_spline3dinterpolant_owner(rhs)
|
|
{
|
|
}
|
|
|
|
spline3dinterpolant& spline3dinterpolant::operator=(const spline3dinterpolant &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_spline3dinterpolant_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
spline3dinterpolant::~spline3dinterpolant()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the trilinear or tricubic spline at
|
|
the given point (X,Y,Z).
|
|
|
|
INPUT PARAMETERS:
|
|
C - coefficients table.
|
|
Built by BuildBilinearSpline or BuildBicubicSpline.
|
|
X, Y,
|
|
Z - point
|
|
|
|
Result:
|
|
S(x,y,z)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline3dcalc(const spline3dinterpolant &c, const double x, const double y, const double z)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
double result = alglib_impl::spline3dcalc(const_cast<alglib_impl::spline3dinterpolant*>(c.c_ptr()), x, y, z, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline argument.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant
|
|
AX, BX - transformation coefficients: x = A*u + B
|
|
AY, BY - transformation coefficients: y = A*v + B
|
|
AZ, BZ - transformation coefficients: z = A*w + B
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dlintransxyz(const spline3dinterpolant &c, const double ax, const double bx, const double ay, const double by, const double az, const double bz)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline3dlintransxyz(const_cast<alglib_impl::spline3dinterpolant*>(c.c_ptr()), ax, bx, ay, by, az, bz, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: S2(x,y) = A*S(x,y,z) + B
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dlintransf(const spline3dinterpolant &c, const double a, const double b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline3dlintransf(const_cast<alglib_impl::spline3dinterpolant*>(c.c_ptr()), a, b, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
Trilinear spline resampling
|
|
|
|
INPUT PARAMETERS:
|
|
A - array[0..OldXCount*OldYCount*OldZCount-1], function
|
|
values at the old grid, :
|
|
A[0] x=0,y=0,z=0
|
|
A[1] x=1,y=0,z=0
|
|
A[..] ...
|
|
A[..] x=oldxcount-1,y=0,z=0
|
|
A[..] x=0,y=1,z=0
|
|
A[..] ...
|
|
...
|
|
OldZCount - old Z-count, OldZCount>1
|
|
OldYCount - old Y-count, OldYCount>1
|
|
OldXCount - old X-count, OldXCount>1
|
|
NewZCount - new Z-count, NewZCount>1
|
|
NewYCount - new Y-count, NewYCount>1
|
|
NewXCount - new X-count, NewXCount>1
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - array[0..NewXCount*NewYCount*NewZCount-1], function
|
|
values at the new grid:
|
|
B[0] x=0,y=0,z=0
|
|
B[1] x=1,y=0,z=0
|
|
B[..] ...
|
|
B[..] x=newxcount-1,y=0,z=0
|
|
B[..] x=0,y=1,z=0
|
|
B[..] ...
|
|
...
|
|
|
|
-- ALGLIB routine --
|
|
26.04.2012
|
|
Copyright by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dresampletrilinear(const real_1d_array &a, const ae_int_t oldzcount, const ae_int_t oldycount, const ae_int_t oldxcount, const ae_int_t newzcount, const ae_int_t newycount, const ae_int_t newxcount, real_1d_array &b)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline3dresampletrilinear(const_cast<alglib_impl::ae_vector*>(a.c_ptr()), oldzcount, oldycount, oldxcount, newzcount, newycount, newxcount, const_cast<alglib_impl::ae_vector*>(b.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine builds trilinear vector-valued spline.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline abscissas, array[0..N-1]
|
|
Y - spline ordinates, array[0..M-1]
|
|
Z - spline applicates, array[0..L-1]
|
|
F - function values, array[0..M*N*L*D-1]:
|
|
* first D elements store D values at (X[0],Y[0],Z[0])
|
|
* next D elements store D values at (X[1],Y[0],Z[0])
|
|
* next D elements store D values at (X[2],Y[0],Z[0])
|
|
* ...
|
|
* next D elements store D values at (X[0],Y[1],Z[0])
|
|
* next D elements store D values at (X[1],Y[1],Z[0])
|
|
* next D elements store D values at (X[2],Y[1],Z[0])
|
|
* ...
|
|
* next D elements store D values at (X[0],Y[0],Z[1])
|
|
* next D elements store D values at (X[1],Y[0],Z[1])
|
|
* next D elements store D values at (X[2],Y[0],Z[1])
|
|
* ...
|
|
* general form - D function values at (X[i],Y[j]) are stored
|
|
at F[D*(N*(M*K+J)+I)...D*(N*(M*K+J)+I)+D-1].
|
|
M,N,
|
|
L - grid size, M>=2, N>=2, L>=2
|
|
D - vector dimension, D>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dbuildtrilinearv(const real_1d_array &x, const ae_int_t n, const real_1d_array &y, const ae_int_t m, const real_1d_array &z, const ae_int_t l, const real_1d_array &f, const ae_int_t d, spline3dinterpolant &c)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline3dbuildtrilinearv(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), m, const_cast<alglib_impl::ae_vector*>(z.c_ptr()), l, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), d, const_cast<alglib_impl::spline3dinterpolant*>(c.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y,Z).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y,
|
|
Z - point
|
|
F - output buffer, possibly preallocated array. In case array size
|
|
is large enough to store result, it is not reallocated. Array
|
|
which is too short will be reallocated
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] (or larger) which stores function values
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dcalcvbuf(const spline3dinterpolant &c, const double x, const double y, const double z, real_1d_array &f)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline3dcalcvbuf(const_cast<alglib_impl::spline3dinterpolant*>(c.c_ptr()), x, y, z, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates trilinear or tricubic vector-valued spline at the
|
|
given point (X,Y,Z).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y,
|
|
Z - point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] which stores function values. F is out-parameter and
|
|
it is reallocated after call to this function. In case you
|
|
want to reuse previously allocated F, you may use
|
|
Spline2DCalcVBuf(), which reallocates F only when it is too
|
|
small.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dcalcv(const spline3dinterpolant &c, const double x, const double y, const double z, real_1d_array &f)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline3dcalcv(const_cast<alglib_impl::spline3dinterpolant*>(c.c_ptr()), x, y, z, const_cast<alglib_impl::ae_vector*>(f.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine unpacks tri-dimensional spline into the coefficients table
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
Result:
|
|
N - grid size (X)
|
|
M - grid size (Y)
|
|
L - grid size (Z)
|
|
D - number of components
|
|
SType- spline type. Currently, only one spline type is supported:
|
|
trilinear spline, as indicated by SType=1.
|
|
Tbl - spline coefficients: [0..(N-1)*(M-1)*(L-1)*D-1, 0..13].
|
|
For T=0..D-1 (component index), I = 0...N-2 (x index),
|
|
J=0..M-2 (y index), K=0..L-2 (z index):
|
|
Q := T + I*D + J*D*(N-1) + K*D*(N-1)*(M-1),
|
|
|
|
Q-th row stores decomposition for T-th component of the
|
|
vector-valued function
|
|
|
|
Tbl[Q,0] = X[i]
|
|
Tbl[Q,1] = X[i+1]
|
|
Tbl[Q,2] = Y[j]
|
|
Tbl[Q,3] = Y[j+1]
|
|
Tbl[Q,4] = Z[k]
|
|
Tbl[Q,5] = Z[k+1]
|
|
|
|
Tbl[Q,6] = C000
|
|
Tbl[Q,7] = C100
|
|
Tbl[Q,8] = C010
|
|
Tbl[Q,9] = C110
|
|
Tbl[Q,10]= C001
|
|
Tbl[Q,11]= C101
|
|
Tbl[Q,12]= C011
|
|
Tbl[Q,13]= C111
|
|
On each grid square spline is equals to:
|
|
S(x) = SUM(c[i,j,k]*(x^i)*(y^j)*(z^k), i=0..1, j=0..1, k=0..1)
|
|
t = x-x[j]
|
|
u = y-y[i]
|
|
v = z-z[k]
|
|
|
|
NOTE: format of Tbl is given for SType=1. Future versions of
|
|
ALGLIB can use different formats for different values of
|
|
SType.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dunpackv(const spline3dinterpolant &c, ae_int_t &n, ae_int_t &m, ae_int_t &l, ae_int_t &d, ae_int_t &stype, real_2d_array &tbl)
|
|
{
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
try
|
|
{
|
|
alglib_impl::spline3dunpackv(const_cast<alglib_impl::spline3dinterpolant*>(c.c_ptr()), &n, &m, &l, &d, &stype, const_cast<alglib_impl::ae_matrix*>(tbl.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
catch(alglib_impl::ae_error_type)
|
|
{
|
|
throw ap_error(_alglib_env_state.error_msg);
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// THIS SECTION CONTAINS IMPLEMENTATION OF COMPUTATIONAL CORE
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
namespace alglib_impl
|
|
{
|
|
static double idwint_idwqfactor = 1.5;
|
|
static ae_int_t idwint_idwkmin = 5;
|
|
static double idwint_idwcalcq(idwinterpolant* z,
|
|
/* Real */ ae_vector* x,
|
|
ae_int_t k,
|
|
ae_state *_state);
|
|
static void idwint_idwinit1(ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t d,
|
|
ae_int_t nq,
|
|
ae_int_t nw,
|
|
idwinterpolant* z,
|
|
ae_state *_state);
|
|
static void idwint_idwinternalsolver(/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
/* Real */ ae_vector* temp,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* x,
|
|
double* taskrcond,
|
|
ae_state *_state);
|
|
|
|
|
|
static void ratint_barycentricnormalize(barycentricinterpolant* b,
|
|
ae_state *_state);
|
|
|
|
|
|
|
|
|
|
static void spline1d_spline1dgriddiffcubicinternal(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
/* Real */ ae_vector* d,
|
|
/* Real */ ae_vector* a1,
|
|
/* Real */ ae_vector* a2,
|
|
/* Real */ ae_vector* a3,
|
|
/* Real */ ae_vector* b,
|
|
/* Real */ ae_vector* dt,
|
|
ae_state *_state);
|
|
static void spline1d_heapsortpoints(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_state *_state);
|
|
static void spline1d_heapsortppoints(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Integer */ ae_vector* p,
|
|
ae_int_t n,
|
|
ae_state *_state);
|
|
static void spline1d_solvetridiagonal(/* Real */ ae_vector* a,
|
|
/* Real */ ae_vector* b,
|
|
/* Real */ ae_vector* c,
|
|
/* Real */ ae_vector* d,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* x,
|
|
ae_state *_state);
|
|
static void spline1d_solvecyclictridiagonal(/* Real */ ae_vector* a,
|
|
/* Real */ ae_vector* b,
|
|
/* Real */ ae_vector* c,
|
|
/* Real */ ae_vector* d,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* x,
|
|
ae_state *_state);
|
|
static double spline1d_diffthreepoint(double t,
|
|
double x0,
|
|
double f0,
|
|
double x1,
|
|
double f1,
|
|
double x2,
|
|
double f2,
|
|
ae_state *_state);
|
|
static void spline1d_hermitecalc(double p0,
|
|
double m0,
|
|
double p1,
|
|
double m1,
|
|
double t,
|
|
double* s,
|
|
double* ds,
|
|
ae_state *_state);
|
|
static double spline1d_rescaleval(double a0,
|
|
double b0,
|
|
double a1,
|
|
double b1,
|
|
double t,
|
|
ae_state *_state);
|
|
|
|
|
|
static void lsfit_spline1dfitinternal(ae_int_t st,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state);
|
|
static void lsfit_lsfitlinearinternal(/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state);
|
|
static void lsfit_lsfitclearrequestfields(lsfitstate* state,
|
|
ae_state *_state);
|
|
static void lsfit_barycentriccalcbasis(barycentricinterpolant* b,
|
|
double t,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state);
|
|
static void lsfit_internalchebyshevfit(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state);
|
|
static void lsfit_barycentricfitwcfixedd(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t d,
|
|
ae_int_t* info,
|
|
barycentricinterpolant* b,
|
|
barycentricfitreport* rep,
|
|
ae_state *_state);
|
|
static void lsfit_clearreport(lsfitreport* rep, ae_state *_state);
|
|
static void lsfit_estimateerrors(/* Real */ ae_matrix* f1,
|
|
/* Real */ ae_vector* f0,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* s,
|
|
ae_int_t n,
|
|
ae_int_t k,
|
|
lsfitreport* rep,
|
|
/* Real */ ae_matrix* z,
|
|
ae_int_t zkind,
|
|
ae_state *_state);
|
|
|
|
|
|
static void pspline_pspline2par(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t pt,
|
|
/* Real */ ae_vector* p,
|
|
ae_state *_state);
|
|
static void pspline_pspline3par(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t pt,
|
|
/* Real */ ae_vector* p,
|
|
ae_state *_state);
|
|
|
|
|
|
static double rbf_eps = 1.0E-6;
|
|
static ae_int_t rbf_mxnx = 3;
|
|
static double rbf_rbffarradius = 6;
|
|
static double rbf_rbfnearradius = 2.1;
|
|
static double rbf_rbfmlradius = 3;
|
|
static ae_int_t rbf_rbffirstversion = 0;
|
|
static void rbf_rbfgridpoints(rbfmodel* s, ae_state *_state);
|
|
static void rbf_rbfradnn(rbfmodel* s,
|
|
double q,
|
|
double z,
|
|
ae_state *_state);
|
|
static ae_bool rbf_buildlinearmodel(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
ae_int_t n,
|
|
ae_int_t ny,
|
|
ae_int_t modeltype,
|
|
/* Real */ ae_matrix* v,
|
|
ae_state *_state);
|
|
static void rbf_buildrbfmodellsqr(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
/* Real */ ae_matrix* xc,
|
|
/* Real */ ae_vector* r,
|
|
ae_int_t n,
|
|
ae_int_t nc,
|
|
ae_int_t ny,
|
|
kdtree* pointstree,
|
|
kdtree* centerstree,
|
|
double epsort,
|
|
double epserr,
|
|
ae_int_t maxits,
|
|
ae_int_t* gnnz,
|
|
ae_int_t* snnz,
|
|
/* Real */ ae_matrix* w,
|
|
ae_int_t* info,
|
|
ae_int_t* iterationscount,
|
|
ae_int_t* nmv,
|
|
ae_state *_state);
|
|
static void rbf_buildrbfmlayersmodellsqr(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
/* Real */ ae_matrix* xc,
|
|
double rval,
|
|
/* Real */ ae_vector* r,
|
|
ae_int_t n,
|
|
ae_int_t* nc,
|
|
ae_int_t ny,
|
|
ae_int_t nlayers,
|
|
kdtree* centerstree,
|
|
double epsort,
|
|
double epserr,
|
|
ae_int_t maxits,
|
|
double lambdav,
|
|
ae_int_t* annz,
|
|
/* Real */ ae_matrix* w,
|
|
ae_int_t* info,
|
|
ae_int_t* iterationscount,
|
|
ae_int_t* nmv,
|
|
ae_state *_state);
|
|
|
|
|
|
static void spline2d_bicubiccalcderivatives(/* Real */ ae_matrix* a,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t m,
|
|
ae_int_t n,
|
|
/* Real */ ae_matrix* dx,
|
|
/* Real */ ae_matrix* dy,
|
|
/* Real */ ae_matrix* dxy,
|
|
ae_state *_state);
|
|
|
|
|
|
static void spline3d_spline3ddiff(spline3dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
double z,
|
|
double* f,
|
|
double* fx,
|
|
double* fy,
|
|
double* fxy,
|
|
ae_state *_state);
|
|
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
IDW interpolation
|
|
|
|
INPUT PARAMETERS:
|
|
Z - IDW interpolant built with one of model building
|
|
subroutines.
|
|
X - array[0..NX-1], interpolation point
|
|
|
|
Result:
|
|
IDW interpolant Z(X)
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc(idwinterpolant* z,
|
|
/* Real */ ae_vector* x,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
double r;
|
|
double s;
|
|
double w;
|
|
double v1;
|
|
double v2;
|
|
double d0;
|
|
double di;
|
|
double result;
|
|
|
|
|
|
|
|
/*
|
|
* these initializers are not really necessary,
|
|
* but without them compiler complains about uninitialized locals
|
|
*/
|
|
k = 0;
|
|
|
|
/*
|
|
* Query
|
|
*/
|
|
if( z->modeltype==0 )
|
|
{
|
|
|
|
/*
|
|
* NQ/NW-based model
|
|
*/
|
|
k = kdtreequeryknn(&z->tree, x, z->nw, ae_true, _state);
|
|
kdtreequeryresultsdistances(&z->tree, &z->rbuf, _state);
|
|
kdtreequeryresultstags(&z->tree, &z->tbuf, _state);
|
|
}
|
|
if( z->modeltype==1 )
|
|
{
|
|
|
|
/*
|
|
* R-based model
|
|
*/
|
|
k = kdtreequeryrnn(&z->tree, x, z->r, ae_true, _state);
|
|
kdtreequeryresultsdistances(&z->tree, &z->rbuf, _state);
|
|
kdtreequeryresultstags(&z->tree, &z->tbuf, _state);
|
|
if( k<idwint_idwkmin )
|
|
{
|
|
|
|
/*
|
|
* we need at least IDWKMin points
|
|
*/
|
|
k = kdtreequeryknn(&z->tree, x, idwint_idwkmin, ae_true, _state);
|
|
kdtreequeryresultsdistances(&z->tree, &z->rbuf, _state);
|
|
kdtreequeryresultstags(&z->tree, &z->tbuf, _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* initialize weights for linear/quadratic members calculation.
|
|
*
|
|
* NOTE 1: weights are calculated using NORMALIZED modified
|
|
* Shepard's formula. Original formula gives w(i) = sqr((R-di)/(R*di)),
|
|
* where di is i-th distance, R is max(di). Modified formula have
|
|
* following form:
|
|
* w_mod(i) = 1, if di=d0
|
|
* w_mod(i) = w(i)/w(0), if di<>d0
|
|
*
|
|
* NOTE 2: self-match is USED for this query
|
|
*
|
|
* NOTE 3: last point almost always gain zero weight, but it MUST
|
|
* be used for fitting because sometimes it will gain NON-ZERO
|
|
* weight - for example, when all distances are equal.
|
|
*/
|
|
r = z->rbuf.ptr.p_double[k-1];
|
|
d0 = z->rbuf.ptr.p_double[0];
|
|
result = 0;
|
|
s = 0;
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
di = z->rbuf.ptr.p_double[i];
|
|
if( ae_fp_eq(di,d0) )
|
|
{
|
|
|
|
/*
|
|
* distance is equal to shortest, set it 1.0
|
|
* without explicitly calculating (which would give
|
|
* us same result, but 'll expose us to the risk of
|
|
* division by zero).
|
|
*/
|
|
w = 1;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* use normalized formula
|
|
*/
|
|
v1 = (r-di)/(r-d0);
|
|
v2 = d0/di;
|
|
w = ae_sqr(v1*v2, _state);
|
|
}
|
|
result = result+w*idwint_idwcalcq(z, x, z->tbuf.ptr.p_int[i], _state);
|
|
s = s+w;
|
|
}
|
|
result = result/s;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW interpolant using modified Shepard method for uniform point
|
|
distributions.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - X and Y values, array[0..N-1,0..NX].
|
|
First NX columns contain X-values, last column contain
|
|
Y-values.
|
|
N - number of nodes, N>0.
|
|
NX - space dimension, NX>=1.
|
|
D - nodal function type, either:
|
|
* 0 constant model. Just for demonstration only, worst
|
|
model ever.
|
|
* 1 linear model, least squares fitting. Simpe model for
|
|
datasets too small for quadratic models
|
|
* 2 quadratic model, least squares fitting. Best model
|
|
available (if your dataset is large enough).
|
|
* -1 "fast" linear model, use with caution!!! It is
|
|
significantly faster than linear/quadratic and better
|
|
than constant model. But it is less robust (especially
|
|
in the presence of noise).
|
|
NQ - number of points used to calculate nodal functions (ignored
|
|
for constant models). NQ should be LARGER than:
|
|
* max(1.5*(1+NX),2^NX+1) for linear model,
|
|
* max(3/4*(NX+2)*(NX+1),2^NX+1) for quadratic model.
|
|
Values less than this threshold will be silently increased.
|
|
NW - number of points used to calculate weights and to interpolate.
|
|
Required: >=2^NX+1, values less than this threshold will be
|
|
silently increased.
|
|
Recommended value: about 2*NQ
|
|
|
|
OUTPUT PARAMETERS:
|
|
Z - IDW interpolant.
|
|
|
|
NOTES:
|
|
* best results are obtained with quadratic models, worst - with constant
|
|
models
|
|
* when N is large, NQ and NW must be significantly smaller than N both
|
|
to obtain optimal performance and to obtain optimal accuracy. In 2 or
|
|
3-dimensional tasks NQ=15 and NW=25 are good values to start with.
|
|
* NQ and NW may be greater than N. In such cases they will be
|
|
automatically decreased.
|
|
* this subroutine is always succeeds (as long as correct parameters are
|
|
passed).
|
|
* see 'Multivariate Interpolation of Large Sets of Scattered Data' by
|
|
Robert J. Renka for more information on this algorithm.
|
|
* this subroutine assumes that point distribution is uniform at the small
|
|
scales. If it isn't - for example, points are concentrated along
|
|
"lines", but "lines" distribution is uniform at the larger scale - then
|
|
you should use IDWBuildModifiedShepardR()
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildmodifiedshepard(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t d,
|
|
ae_int_t nq,
|
|
ae_int_t nw,
|
|
idwinterpolant* z,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t j2;
|
|
ae_int_t j3;
|
|
double v;
|
|
double r;
|
|
double s;
|
|
double d0;
|
|
double di;
|
|
double v1;
|
|
double v2;
|
|
ae_int_t nc;
|
|
ae_int_t offs;
|
|
ae_vector x;
|
|
ae_vector qrbuf;
|
|
ae_matrix qxybuf;
|
|
ae_vector y;
|
|
ae_matrix fmatrix;
|
|
ae_vector w;
|
|
ae_vector qsol;
|
|
ae_vector temp;
|
|
ae_vector tags;
|
|
ae_int_t info;
|
|
double taskrcond;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_idwinterpolant_clear(z);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&qrbuf, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&qxybuf, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&fmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&qsol, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&temp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* these initializers are not really necessary,
|
|
* but without them compiler complains about uninitialized locals
|
|
*/
|
|
nc = 0;
|
|
|
|
/*
|
|
* assertions
|
|
*/
|
|
ae_assert(n>0, "IDWBuildModifiedShepard: N<=0!", _state);
|
|
ae_assert(nx>=1, "IDWBuildModifiedShepard: NX<1!", _state);
|
|
ae_assert(d>=-1&&d<=2, "IDWBuildModifiedShepard: D<>-1 and D<>0 and D<>1 and D<>2!", _state);
|
|
|
|
/*
|
|
* Correct parameters if needed
|
|
*/
|
|
if( d==1 )
|
|
{
|
|
nq = ae_maxint(nq, ae_iceil(idwint_idwqfactor*(1+nx), _state)+1, _state);
|
|
nq = ae_maxint(nq, ae_round(ae_pow(2, nx, _state), _state)+1, _state);
|
|
}
|
|
if( d==2 )
|
|
{
|
|
nq = ae_maxint(nq, ae_iceil(idwint_idwqfactor*(nx+2)*(nx+1)/2, _state)+1, _state);
|
|
nq = ae_maxint(nq, ae_round(ae_pow(2, nx, _state), _state)+1, _state);
|
|
}
|
|
nw = ae_maxint(nw, ae_round(ae_pow(2, nx, _state), _state)+1, _state);
|
|
nq = ae_minint(nq, n, _state);
|
|
nw = ae_minint(nw, n, _state);
|
|
|
|
/*
|
|
* primary initialization of Z
|
|
*/
|
|
idwint_idwinit1(n, nx, d, nq, nw, z, _state);
|
|
z->modeltype = 0;
|
|
|
|
/*
|
|
* Create KD-tree
|
|
*/
|
|
ae_vector_set_length(&tags, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
tags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(xy, &tags, n, nx, 1, 2, &z->tree, _state);
|
|
|
|
/*
|
|
* build nodal functions
|
|
*/
|
|
ae_vector_set_length(&temp, nq+1, _state);
|
|
ae_vector_set_length(&x, nx, _state);
|
|
ae_vector_set_length(&qrbuf, nq, _state);
|
|
ae_matrix_set_length(&qxybuf, nq, nx+1, _state);
|
|
if( d==-1 )
|
|
{
|
|
ae_vector_set_length(&w, nq, _state);
|
|
}
|
|
if( d==1 )
|
|
{
|
|
ae_vector_set_length(&y, nq, _state);
|
|
ae_vector_set_length(&w, nq, _state);
|
|
ae_vector_set_length(&qsol, nx, _state);
|
|
|
|
/*
|
|
* NX for linear members,
|
|
* 1 for temporary storage
|
|
*/
|
|
ae_matrix_set_length(&fmatrix, nq, nx+1, _state);
|
|
}
|
|
if( d==2 )
|
|
{
|
|
ae_vector_set_length(&y, nq, _state);
|
|
ae_vector_set_length(&w, nq, _state);
|
|
ae_vector_set_length(&qsol, nx+ae_round(nx*(nx+1)*0.5, _state), _state);
|
|
|
|
/*
|
|
* NX for linear members,
|
|
* Round(NX*(NX+1)*0.5) for quadratic model,
|
|
* 1 for temporary storage
|
|
*/
|
|
ae_matrix_set_length(&fmatrix, nq, nx+ae_round(nx*(nx+1)*0.5, _state)+1, _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Initialize center and function value.
|
|
* If D=0 it is all what we need
|
|
*/
|
|
ae_v_move(&z->q.ptr.pp_double[i][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx));
|
|
if( d==0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* calculate weights for linear/quadratic members calculation.
|
|
*
|
|
* NOTE 1: weights are calculated using NORMALIZED modified
|
|
* Shepard's formula. Original formula is w(i) = sqr((R-di)/(R*di)),
|
|
* where di is i-th distance, R is max(di). Modified formula have
|
|
* following form:
|
|
* w_mod(i) = 1, if di=d0
|
|
* w_mod(i) = w(i)/w(0), if di<>d0
|
|
*
|
|
* NOTE 2: self-match is NOT used for this query
|
|
*
|
|
* NOTE 3: last point almost always gain zero weight, but it MUST
|
|
* be used for fitting because sometimes it will gain NON-ZERO
|
|
* weight - for example, when all distances are equal.
|
|
*/
|
|
ae_v_move(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
|
|
k = kdtreequeryknn(&z->tree, &x, nq, ae_false, _state);
|
|
kdtreequeryresultsxy(&z->tree, &qxybuf, _state);
|
|
kdtreequeryresultsdistances(&z->tree, &qrbuf, _state);
|
|
r = qrbuf.ptr.p_double[k-1];
|
|
d0 = qrbuf.ptr.p_double[0];
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
di = qrbuf.ptr.p_double[j];
|
|
if( ae_fp_eq(di,d0) )
|
|
{
|
|
|
|
/*
|
|
* distance is equal to shortest, set it 1.0
|
|
* without explicitly calculating (which would give
|
|
* us same result, but 'll expose us to the risk of
|
|
* division by zero).
|
|
*/
|
|
w.ptr.p_double[j] = 1;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* use normalized formula
|
|
*/
|
|
v1 = (r-di)/(r-d0);
|
|
v2 = d0/di;
|
|
w.ptr.p_double[j] = ae_sqr(v1*v2, _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* calculate linear/quadratic members
|
|
*/
|
|
if( d==-1 )
|
|
{
|
|
|
|
/*
|
|
* "Fast" linear nodal function calculated using
|
|
* inverse distance weighting
|
|
*/
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
x.ptr.p_double[j] = 0;
|
|
}
|
|
s = 0;
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
|
|
/*
|
|
* calculate J-th inverse distance weighted gradient:
|
|
* grad_k = (y_j-y_k)*(x_j-x_k)/sqr(norm(x_j-x_k))
|
|
* grad = sum(wk*grad_k)/sum(w_k)
|
|
*/
|
|
v = 0;
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
v = v+ae_sqr(qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2], _state);
|
|
}
|
|
|
|
/*
|
|
* Although x_j<>x_k, sqr(norm(x_j-x_k)) may be zero due to
|
|
* underflow. If it is, we assume than J-th gradient is zero
|
|
* (i.e. don't add anything)
|
|
*/
|
|
if( ae_fp_neq(v,0) )
|
|
{
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
x.ptr.p_double[j2] = x.ptr.p_double[j2]+w.ptr.p_double[j]*(qxybuf.ptr.pp_double[j][nx]-xy->ptr.pp_double[i][nx])*(qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2])/v;
|
|
}
|
|
}
|
|
s = s+w.ptr.p_double[j];
|
|
}
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
z->q.ptr.pp_double[i][nx+1+j] = x.ptr.p_double[j]/s;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Least squares models: build
|
|
*/
|
|
if( d==1 )
|
|
{
|
|
|
|
/*
|
|
* Linear nodal function calculated using
|
|
* least squares fitting to its neighbors
|
|
*/
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][j2] = qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2];
|
|
}
|
|
y.ptr.p_double[j] = qxybuf.ptr.pp_double[j][nx]-xy->ptr.pp_double[i][nx];
|
|
}
|
|
nc = nx;
|
|
}
|
|
if( d==2 )
|
|
{
|
|
|
|
/*
|
|
* Quadratic nodal function calculated using
|
|
* least squares fitting to its neighbors
|
|
*/
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
offs = 0;
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][offs] = qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2];
|
|
offs = offs+1;
|
|
}
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
for(j3=j2; j3<=nx-1; j3++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][offs] = (qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2])*(qxybuf.ptr.pp_double[j][j3]-xy->ptr.pp_double[i][j3]);
|
|
offs = offs+1;
|
|
}
|
|
}
|
|
y.ptr.p_double[j] = qxybuf.ptr.pp_double[j][nx]-xy->ptr.pp_double[i][nx];
|
|
}
|
|
nc = nx+ae_round(nx*(nx+1)*0.5, _state);
|
|
}
|
|
idwint_idwinternalsolver(&y, &w, &fmatrix, &temp, k, nc, &info, &qsol, &taskrcond, _state);
|
|
|
|
/*
|
|
* Least squares models: copy results
|
|
*/
|
|
if( info>0 )
|
|
{
|
|
|
|
/*
|
|
* LLS task is solved, copy results
|
|
*/
|
|
z->debugworstrcond = ae_minreal(z->debugworstrcond, taskrcond, _state);
|
|
z->debugbestrcond = ae_maxreal(z->debugbestrcond, taskrcond, _state);
|
|
for(j=0; j<=nc-1; j++)
|
|
{
|
|
z->q.ptr.pp_double[i][nx+1+j] = qsol.ptr.p_double[j];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Solver failure, very strange, but we will use
|
|
* zero values to handle it.
|
|
*/
|
|
z->debugsolverfailures = z->debugsolverfailures+1;
|
|
for(j=0; j<=nc-1; j++)
|
|
{
|
|
z->q.ptr.pp_double[i][nx+1+j] = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW interpolant using modified Shepard method for non-uniform datasets.
|
|
|
|
This type of model uses constant nodal functions and interpolates using
|
|
all nodes which are closer than user-specified radius R. It may be used
|
|
when points distribution is non-uniform at the small scale, but it is at
|
|
the distances as large as R.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - X and Y values, array[0..N-1,0..NX].
|
|
First NX columns contain X-values, last column contain
|
|
Y-values.
|
|
N - number of nodes, N>0.
|
|
NX - space dimension, NX>=1.
|
|
R - radius, R>0
|
|
|
|
OUTPUT PARAMETERS:
|
|
Z - IDW interpolant.
|
|
|
|
NOTES:
|
|
* if there is less than IDWKMin points within R-ball, algorithm selects
|
|
IDWKMin closest ones, so that continuity properties of interpolant are
|
|
preserved even far from points.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 11.04.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildmodifiedshepardr(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
double r,
|
|
idwinterpolant* z,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector tags;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_idwinterpolant_clear(z);
|
|
ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* assertions
|
|
*/
|
|
ae_assert(n>0, "IDWBuildModifiedShepardR: N<=0!", _state);
|
|
ae_assert(nx>=1, "IDWBuildModifiedShepardR: NX<1!", _state);
|
|
ae_assert(ae_fp_greater(r,0), "IDWBuildModifiedShepardR: R<=0!", _state);
|
|
|
|
/*
|
|
* primary initialization of Z
|
|
*/
|
|
idwint_idwinit1(n, nx, 0, 0, n, z, _state);
|
|
z->modeltype = 1;
|
|
z->r = r;
|
|
|
|
/*
|
|
* Create KD-tree
|
|
*/
|
|
ae_vector_set_length(&tags, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
tags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(xy, &tags, n, nx, 1, 2, &z->tree, _state);
|
|
|
|
/*
|
|
* build nodal functions
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_v_move(&z->q.ptr.pp_double[i][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx));
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW model for noisy data.
|
|
|
|
This subroutine may be used to handle noisy data, i.e. data with noise in
|
|
OUTPUT values. It differs from IDWBuildModifiedShepard() in the following
|
|
aspects:
|
|
* nodal functions are not constrained to pass through nodes: Qi(xi)<>yi,
|
|
i.e. we have fitting instead of interpolation.
|
|
* weights which are used during least squares fitting stage are all equal
|
|
to 1.0 (independently of distance)
|
|
* "fast"-linear or constant nodal functions are not supported (either not
|
|
robust enough or too rigid)
|
|
|
|
This problem require far more complex tuning than interpolation problems.
|
|
Below you can find some recommendations regarding this problem:
|
|
* focus on tuning NQ; it controls noise reduction. As for NW, you can just
|
|
make it equal to 2*NQ.
|
|
* you can use cross-validation to determine optimal NQ.
|
|
* optimal NQ is a result of complex tradeoff between noise level (more
|
|
noise = larger NQ required) and underlying function complexity (given
|
|
fixed N, larger NQ means smoothing of compex features in the data). For
|
|
example, NQ=N will reduce noise to the minimum level possible, but you
|
|
will end up with just constant/linear/quadratic (depending on D) least
|
|
squares model for the whole dataset.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - X and Y values, array[0..N-1,0..NX].
|
|
First NX columns contain X-values, last column contain
|
|
Y-values.
|
|
N - number of nodes, N>0.
|
|
NX - space dimension, NX>=1.
|
|
D - nodal function degree, either:
|
|
* 1 linear model, least squares fitting. Simpe model for
|
|
datasets too small for quadratic models (or for very
|
|
noisy problems).
|
|
* 2 quadratic model, least squares fitting. Best model
|
|
available (if your dataset is large enough).
|
|
NQ - number of points used to calculate nodal functions. NQ should
|
|
be significantly larger than 1.5 times the number of
|
|
coefficients in a nodal function to overcome effects of noise:
|
|
* larger than 1.5*(1+NX) for linear model,
|
|
* larger than 3/4*(NX+2)*(NX+1) for quadratic model.
|
|
Values less than this threshold will be silently increased.
|
|
NW - number of points used to calculate weights and to interpolate.
|
|
Required: >=2^NX+1, values less than this threshold will be
|
|
silently increased.
|
|
Recommended value: about 2*NQ or larger
|
|
|
|
OUTPUT PARAMETERS:
|
|
Z - IDW interpolant.
|
|
|
|
NOTES:
|
|
* best results are obtained with quadratic models, linear models are not
|
|
recommended to use unless you are pretty sure that it is what you want
|
|
* this subroutine is always succeeds (as long as correct parameters are
|
|
passed).
|
|
* see 'Multivariate Interpolation of Large Sets of Scattered Data' by
|
|
Robert J. Renka for more information on this algorithm.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildnoisy(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t d,
|
|
ae_int_t nq,
|
|
ae_int_t nw,
|
|
idwinterpolant* z,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t j2;
|
|
ae_int_t j3;
|
|
double v;
|
|
ae_int_t nc;
|
|
ae_int_t offs;
|
|
double taskrcond;
|
|
ae_vector x;
|
|
ae_vector qrbuf;
|
|
ae_matrix qxybuf;
|
|
ae_vector y;
|
|
ae_vector w;
|
|
ae_matrix fmatrix;
|
|
ae_vector qsol;
|
|
ae_vector tags;
|
|
ae_vector temp;
|
|
ae_int_t info;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_idwinterpolant_clear(z);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&qrbuf, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&qxybuf, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&fmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&qsol, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&temp, 0, DT_REAL, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* these initializers are not really necessary,
|
|
* but without them compiler complains about uninitialized locals
|
|
*/
|
|
nc = 0;
|
|
|
|
/*
|
|
* assertions
|
|
*/
|
|
ae_assert(n>0, "IDWBuildNoisy: N<=0!", _state);
|
|
ae_assert(nx>=1, "IDWBuildNoisy: NX<1!", _state);
|
|
ae_assert(d>=1&&d<=2, "IDWBuildNoisy: D<>1 and D<>2!", _state);
|
|
|
|
/*
|
|
* Correct parameters if needed
|
|
*/
|
|
if( d==1 )
|
|
{
|
|
nq = ae_maxint(nq, ae_iceil(idwint_idwqfactor*(1+nx), _state)+1, _state);
|
|
}
|
|
if( d==2 )
|
|
{
|
|
nq = ae_maxint(nq, ae_iceil(idwint_idwqfactor*(nx+2)*(nx+1)/2, _state)+1, _state);
|
|
}
|
|
nw = ae_maxint(nw, ae_round(ae_pow(2, nx, _state), _state)+1, _state);
|
|
nq = ae_minint(nq, n, _state);
|
|
nw = ae_minint(nw, n, _state);
|
|
|
|
/*
|
|
* primary initialization of Z
|
|
*/
|
|
idwint_idwinit1(n, nx, d, nq, nw, z, _state);
|
|
z->modeltype = 0;
|
|
|
|
/*
|
|
* Create KD-tree
|
|
*/
|
|
ae_vector_set_length(&tags, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
tags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(xy, &tags, n, nx, 1, 2, &z->tree, _state);
|
|
|
|
/*
|
|
* build nodal functions
|
|
* (special algorithm for noisy data is used)
|
|
*/
|
|
ae_vector_set_length(&temp, nq+1, _state);
|
|
ae_vector_set_length(&x, nx, _state);
|
|
ae_vector_set_length(&qrbuf, nq, _state);
|
|
ae_matrix_set_length(&qxybuf, nq, nx+1, _state);
|
|
if( d==1 )
|
|
{
|
|
ae_vector_set_length(&y, nq, _state);
|
|
ae_vector_set_length(&w, nq, _state);
|
|
ae_vector_set_length(&qsol, 1+nx, _state);
|
|
|
|
/*
|
|
* 1 for constant member,
|
|
* NX for linear members,
|
|
* 1 for temporary storage
|
|
*/
|
|
ae_matrix_set_length(&fmatrix, nq, 1+nx+1, _state);
|
|
}
|
|
if( d==2 )
|
|
{
|
|
ae_vector_set_length(&y, nq, _state);
|
|
ae_vector_set_length(&w, nq, _state);
|
|
ae_vector_set_length(&qsol, 1+nx+ae_round(nx*(nx+1)*0.5, _state), _state);
|
|
|
|
/*
|
|
* 1 for constant member,
|
|
* NX for linear members,
|
|
* Round(NX*(NX+1)*0.5) for quadratic model,
|
|
* 1 for temporary storage
|
|
*/
|
|
ae_matrix_set_length(&fmatrix, nq, 1+nx+ae_round(nx*(nx+1)*0.5, _state)+1, _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Initialize center.
|
|
*/
|
|
ae_v_move(&z->q.ptr.pp_double[i][0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
|
|
|
|
/*
|
|
* Calculate linear/quadratic members
|
|
* using least squares fit
|
|
* NOTE 1: all weight are equal to 1.0
|
|
* NOTE 2: self-match is USED for this query
|
|
*/
|
|
ae_v_move(&x.ptr.p_double[0], 1, &xy->ptr.pp_double[i][0], 1, ae_v_len(0,nx-1));
|
|
k = kdtreequeryknn(&z->tree, &x, nq, ae_true, _state);
|
|
kdtreequeryresultsxy(&z->tree, &qxybuf, _state);
|
|
kdtreequeryresultsdistances(&z->tree, &qrbuf, _state);
|
|
if( d==1 )
|
|
{
|
|
|
|
/*
|
|
* Linear nodal function calculated using
|
|
* least squares fitting to its neighbors
|
|
*/
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][0] = 1.0;
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][1+j2] = qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2];
|
|
}
|
|
y.ptr.p_double[j] = qxybuf.ptr.pp_double[j][nx];
|
|
w.ptr.p_double[j] = 1;
|
|
}
|
|
nc = 1+nx;
|
|
}
|
|
if( d==2 )
|
|
{
|
|
|
|
/*
|
|
* Quadratic nodal function calculated using
|
|
* least squares fitting to its neighbors
|
|
*/
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][0] = 1;
|
|
offs = 1;
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][offs] = qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2];
|
|
offs = offs+1;
|
|
}
|
|
for(j2=0; j2<=nx-1; j2++)
|
|
{
|
|
for(j3=j2; j3<=nx-1; j3++)
|
|
{
|
|
fmatrix.ptr.pp_double[j][offs] = (qxybuf.ptr.pp_double[j][j2]-xy->ptr.pp_double[i][j2])*(qxybuf.ptr.pp_double[j][j3]-xy->ptr.pp_double[i][j3]);
|
|
offs = offs+1;
|
|
}
|
|
}
|
|
y.ptr.p_double[j] = qxybuf.ptr.pp_double[j][nx];
|
|
w.ptr.p_double[j] = 1;
|
|
}
|
|
nc = 1+nx+ae_round(nx*(nx+1)*0.5, _state);
|
|
}
|
|
idwint_idwinternalsolver(&y, &w, &fmatrix, &temp, k, nc, &info, &qsol, &taskrcond, _state);
|
|
|
|
/*
|
|
* Least squares models: copy results
|
|
*/
|
|
if( info>0 )
|
|
{
|
|
|
|
/*
|
|
* LLS task is solved, copy results
|
|
*/
|
|
z->debugworstrcond = ae_minreal(z->debugworstrcond, taskrcond, _state);
|
|
z->debugbestrcond = ae_maxreal(z->debugbestrcond, taskrcond, _state);
|
|
for(j=0; j<=nc-1; j++)
|
|
{
|
|
z->q.ptr.pp_double[i][nx+j] = qsol.ptr.p_double[j];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Solver failure, very strange, but we will use
|
|
* zero values to handle it.
|
|
*/
|
|
z->debugsolverfailures = z->debugsolverfailures+1;
|
|
v = 0;
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
v = v+qxybuf.ptr.pp_double[j][nx];
|
|
}
|
|
z->q.ptr.pp_double[i][nx] = v/k;
|
|
for(j=0; j<=nc-2; j++)
|
|
{
|
|
z->q.ptr.pp_double[i][nx+1+j] = 0;
|
|
}
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine: K-th nodal function calculation
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static double idwint_idwcalcq(idwinterpolant* z,
|
|
/* Real */ ae_vector* x,
|
|
ae_int_t k,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t nx;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t offs;
|
|
double result;
|
|
|
|
|
|
nx = z->nx;
|
|
|
|
/*
|
|
* constant member
|
|
*/
|
|
result = z->q.ptr.pp_double[k][nx];
|
|
|
|
/*
|
|
* linear members
|
|
*/
|
|
if( z->d>=1 )
|
|
{
|
|
for(i=0; i<=nx-1; i++)
|
|
{
|
|
result = result+z->q.ptr.pp_double[k][nx+1+i]*(x->ptr.p_double[i]-z->q.ptr.pp_double[k][i]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* quadratic members
|
|
*/
|
|
if( z->d>=2 )
|
|
{
|
|
offs = nx+1+nx;
|
|
for(i=0; i<=nx-1; i++)
|
|
{
|
|
for(j=i; j<=nx-1; j++)
|
|
{
|
|
result = result+z->q.ptr.pp_double[k][offs]*(x->ptr.p_double[i]-z->q.ptr.pp_double[k][i])*(x->ptr.p_double[j]-z->q.ptr.pp_double[k][j]);
|
|
offs = offs+1;
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Initialization of internal structures.
|
|
|
|
It assumes correctness of all parameters.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.03.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void idwint_idwinit1(ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t d,
|
|
ae_int_t nq,
|
|
ae_int_t nw,
|
|
idwinterpolant* z,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
z->debugsolverfailures = 0;
|
|
z->debugworstrcond = 1.0;
|
|
z->debugbestrcond = 0;
|
|
z->n = n;
|
|
z->nx = nx;
|
|
z->d = 0;
|
|
if( d==1 )
|
|
{
|
|
z->d = 1;
|
|
}
|
|
if( d==2 )
|
|
{
|
|
z->d = 2;
|
|
}
|
|
if( d==-1 )
|
|
{
|
|
z->d = 1;
|
|
}
|
|
z->nw = nw;
|
|
if( d==-1 )
|
|
{
|
|
ae_matrix_set_length(&z->q, n, nx+1+nx, _state);
|
|
}
|
|
if( d==0 )
|
|
{
|
|
ae_matrix_set_length(&z->q, n, nx+1, _state);
|
|
}
|
|
if( d==1 )
|
|
{
|
|
ae_matrix_set_length(&z->q, n, nx+1+nx, _state);
|
|
}
|
|
if( d==2 )
|
|
{
|
|
ae_matrix_set_length(&z->q, n, nx+1+nx+ae_round(nx*(nx+1)*0.5, _state), _state);
|
|
}
|
|
ae_vector_set_length(&z->tbuf, nw, _state);
|
|
ae_vector_set_length(&z->rbuf, nw, _state);
|
|
ae_matrix_set_length(&z->xybuf, nw, nx+1, _state);
|
|
ae_vector_set_length(&z->xbuf, nx, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Linear least squares solver for small tasks.
|
|
|
|
Works faster than standard ALGLIB solver in non-degenerate cases (due to
|
|
absense of internal allocations and optimized row/colums). In degenerate
|
|
cases it calls standard solver, which results in small performance penalty
|
|
associated with preliminary steps.
|
|
|
|
INPUT PARAMETERS:
|
|
Y array[0..N-1]
|
|
W array[0..N-1]
|
|
FMatrix array[0..N-1,0..M], have additional column for temporary
|
|
values
|
|
Temp array[0..N]
|
|
*************************************************************************/
|
|
static void idwint_idwinternalsolver(/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
/* Real */ ae_vector* temp,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* x,
|
|
double* taskrcond,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double v;
|
|
double tau;
|
|
ae_vector b;
|
|
densesolverlsreport srep;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
_densesolverlsreport_init(&srep, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* set up info
|
|
*/
|
|
*info = 1;
|
|
|
|
/*
|
|
* prepare matrix
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
fmatrix->ptr.pp_double[i][m] = y->ptr.p_double[i];
|
|
v = w->ptr.p_double[i];
|
|
ae_v_muld(&fmatrix->ptr.pp_double[i][0], 1, ae_v_len(0,m), v);
|
|
}
|
|
|
|
/*
|
|
* use either fast algorithm or general algorithm
|
|
*/
|
|
if( m<=n )
|
|
{
|
|
|
|
/*
|
|
* QR decomposition
|
|
* We assume that M<=N (we would have called LSFit() otherwise)
|
|
*/
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
if( i<n-1 )
|
|
{
|
|
ae_v_move(&temp->ptr.p_double[1], 1, &fmatrix->ptr.pp_double[i][i], fmatrix->stride, ae_v_len(1,n-i));
|
|
generatereflection(temp, n-i, &tau, _state);
|
|
fmatrix->ptr.pp_double[i][i] = temp->ptr.p_double[1];
|
|
temp->ptr.p_double[1] = 1;
|
|
for(j=i+1; j<=m; j++)
|
|
{
|
|
v = ae_v_dotproduct(&fmatrix->ptr.pp_double[i][j], fmatrix->stride, &temp->ptr.p_double[1], 1, ae_v_len(i,n-1));
|
|
v = tau*v;
|
|
ae_v_subd(&fmatrix->ptr.pp_double[i][j], fmatrix->stride, &temp->ptr.p_double[1], 1, ae_v_len(i,n-1), v);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check condition number
|
|
*/
|
|
*taskrcond = rmatrixtrrcondinf(fmatrix, m, ae_true, ae_false, _state);
|
|
|
|
/*
|
|
* use either fast algorithm for non-degenerate cases
|
|
* or slow algorithm for degenerate cases
|
|
*/
|
|
if( ae_fp_greater(*taskrcond,10000*n*ae_machineepsilon) )
|
|
{
|
|
|
|
/*
|
|
* solve triangular system R*x = FMatrix[0:M-1,M]
|
|
* using fast algorithm, then exit
|
|
*/
|
|
x->ptr.p_double[m-1] = fmatrix->ptr.pp_double[m-1][m]/fmatrix->ptr.pp_double[m-1][m-1];
|
|
for(i=m-2; i>=0; i--)
|
|
{
|
|
v = ae_v_dotproduct(&fmatrix->ptr.pp_double[i][i+1], 1, &x->ptr.p_double[i+1], 1, ae_v_len(i+1,m-1));
|
|
x->ptr.p_double[i] = (fmatrix->ptr.pp_double[i][m]-v)/fmatrix->ptr.pp_double[i][i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* use more general algorithm
|
|
*/
|
|
ae_vector_set_length(&b, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=0; j<=i-1; j++)
|
|
{
|
|
fmatrix->ptr.pp_double[i][j] = 0.0;
|
|
}
|
|
b.ptr.p_double[i] = fmatrix->ptr.pp_double[i][m];
|
|
}
|
|
rmatrixsolvels(fmatrix, m, m, &b, 10000*ae_machineepsilon, info, &srep, x, _state);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* use more general algorithm
|
|
*/
|
|
ae_vector_set_length(&b, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
b.ptr.p_double[i] = fmatrix->ptr.pp_double[i][m];
|
|
}
|
|
rmatrixsolvels(fmatrix, n, m, &b, 10000*ae_machineepsilon, info, &srep, x, _state);
|
|
*taskrcond = srep.r2;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
ae_bool _idwinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwinterpolant *p = (idwinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !_kdtree_init(&p->tree, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->q, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->xbuf, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->tbuf, 0, DT_INT, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->rbuf, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->xybuf, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _idwinterpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwinterpolant *dst = (idwinterpolant*)_dst;
|
|
idwinterpolant *src = (idwinterpolant*)_src;
|
|
dst->n = src->n;
|
|
dst->nx = src->nx;
|
|
dst->d = src->d;
|
|
dst->r = src->r;
|
|
dst->nw = src->nw;
|
|
if( !_kdtree_init_copy(&dst->tree, &src->tree, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->modeltype = src->modeltype;
|
|
if( !ae_matrix_init_copy(&dst->q, &src->q, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->xbuf, &src->xbuf, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->tbuf, &src->tbuf, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->rbuf, &src->rbuf, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->xybuf, &src->xybuf, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->debugsolverfailures = src->debugsolverfailures;
|
|
dst->debugworstrcond = src->debugworstrcond;
|
|
dst->debugbestrcond = src->debugbestrcond;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _idwinterpolant_clear(void* _p)
|
|
{
|
|
idwinterpolant *p = (idwinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_kdtree_clear(&p->tree);
|
|
ae_matrix_clear(&p->q);
|
|
ae_vector_clear(&p->xbuf);
|
|
ae_vector_clear(&p->tbuf);
|
|
ae_vector_clear(&p->rbuf);
|
|
ae_matrix_clear(&p->xybuf);
|
|
}
|
|
|
|
|
|
void _idwinterpolant_destroy(void* _p)
|
|
{
|
|
idwinterpolant *p = (idwinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_kdtree_destroy(&p->tree);
|
|
ae_matrix_destroy(&p->q);
|
|
ae_vector_destroy(&p->xbuf);
|
|
ae_vector_destroy(&p->tbuf);
|
|
ae_vector_destroy(&p->rbuf);
|
|
ae_matrix_destroy(&p->xybuf);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
Rational interpolation using barycentric formula
|
|
|
|
F(t) = SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))
|
|
|
|
Input parameters:
|
|
B - barycentric interpolant built with one of model building
|
|
subroutines.
|
|
T - interpolation point
|
|
|
|
Result:
|
|
barycentric interpolant F(t)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double barycentriccalc(barycentricinterpolant* b,
|
|
double t,
|
|
ae_state *_state)
|
|
{
|
|
double s1;
|
|
double s2;
|
|
double s;
|
|
double v;
|
|
ae_int_t i;
|
|
double result;
|
|
|
|
|
|
ae_assert(!ae_isinf(t, _state), "BarycentricCalc: infinite T!", _state);
|
|
|
|
/*
|
|
* special case: NaN
|
|
*/
|
|
if( ae_isnan(t, _state) )
|
|
{
|
|
result = _state->v_nan;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* special case: N=1
|
|
*/
|
|
if( b->n==1 )
|
|
{
|
|
result = b->sy*b->y.ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Here we assume that task is normalized, i.e.:
|
|
* 1. abs(Y[i])<=1
|
|
* 2. abs(W[i])<=1
|
|
* 3. X[] is ordered
|
|
*/
|
|
s = ae_fabs(t-b->x.ptr.p_double[0], _state);
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
v = b->x.ptr.p_double[i];
|
|
if( ae_fp_eq(v,t) )
|
|
{
|
|
result = b->sy*b->y.ptr.p_double[i];
|
|
return result;
|
|
}
|
|
v = ae_fabs(t-v, _state);
|
|
if( ae_fp_less(v,s) )
|
|
{
|
|
s = v;
|
|
}
|
|
}
|
|
s1 = 0;
|
|
s2 = 0;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
v = s/(t-b->x.ptr.p_double[i]);
|
|
v = v*b->w.ptr.p_double[i];
|
|
s1 = s1+v*b->y.ptr.p_double[i];
|
|
s2 = s2+v;
|
|
}
|
|
result = b->sy*s1/s2;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Differentiation of barycentric interpolant: first derivative.
|
|
|
|
Algorithm used in this subroutine is very robust and should not fail until
|
|
provided with values too close to MaxRealNumber (usually MaxRealNumber/N
|
|
or greater will overflow).
|
|
|
|
INPUT PARAMETERS:
|
|
B - barycentric interpolant built with one of model building
|
|
subroutines.
|
|
T - interpolation point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - barycentric interpolant at T
|
|
DF - first derivative
|
|
|
|
NOTE
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricdiff1(barycentricinterpolant* b,
|
|
double t,
|
|
double* f,
|
|
double* df,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
double vv;
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
double n0;
|
|
double n1;
|
|
double d0;
|
|
double d1;
|
|
double s0;
|
|
double s1;
|
|
double xk;
|
|
double xi;
|
|
double xmin;
|
|
double xmax;
|
|
double xscale1;
|
|
double xoffs1;
|
|
double xscale2;
|
|
double xoffs2;
|
|
double xprev;
|
|
|
|
*f = 0;
|
|
*df = 0;
|
|
|
|
ae_assert(!ae_isinf(t, _state), "BarycentricDiff1: infinite T!", _state);
|
|
|
|
/*
|
|
* special case: NaN
|
|
*/
|
|
if( ae_isnan(t, _state) )
|
|
{
|
|
*f = _state->v_nan;
|
|
*df = _state->v_nan;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* special case: N=1
|
|
*/
|
|
if( b->n==1 )
|
|
{
|
|
*f = b->sy*b->y.ptr.p_double[0];
|
|
*df = 0;
|
|
return;
|
|
}
|
|
if( ae_fp_eq(b->sy,0) )
|
|
{
|
|
*f = 0;
|
|
*df = 0;
|
|
return;
|
|
}
|
|
ae_assert(ae_fp_greater(b->sy,0), "BarycentricDiff1: internal error", _state);
|
|
|
|
/*
|
|
* We assume than N>1 and B.SY>0. Find:
|
|
* 1. pivot point (X[i] closest to T)
|
|
* 2. width of interval containing X[i]
|
|
*/
|
|
v = ae_fabs(b->x.ptr.p_double[0]-t, _state);
|
|
k = 0;
|
|
xmin = b->x.ptr.p_double[0];
|
|
xmax = b->x.ptr.p_double[0];
|
|
for(i=1; i<=b->n-1; i++)
|
|
{
|
|
vv = b->x.ptr.p_double[i];
|
|
if( ae_fp_less(ae_fabs(vv-t, _state),v) )
|
|
{
|
|
v = ae_fabs(vv-t, _state);
|
|
k = i;
|
|
}
|
|
xmin = ae_minreal(xmin, vv, _state);
|
|
xmax = ae_maxreal(xmax, vv, _state);
|
|
}
|
|
|
|
/*
|
|
* pivot point found, calculate dNumerator and dDenominator
|
|
*/
|
|
xscale1 = 1/(xmax-xmin);
|
|
xoffs1 = -xmin/(xmax-xmin)+1;
|
|
xscale2 = 2;
|
|
xoffs2 = -3;
|
|
t = t*xscale1+xoffs1;
|
|
t = t*xscale2+xoffs2;
|
|
xk = b->x.ptr.p_double[k];
|
|
xk = xk*xscale1+xoffs1;
|
|
xk = xk*xscale2+xoffs2;
|
|
v = t-xk;
|
|
n0 = 0;
|
|
n1 = 0;
|
|
d0 = 0;
|
|
d1 = 0;
|
|
xprev = -2;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
xi = b->x.ptr.p_double[i];
|
|
xi = xi*xscale1+xoffs1;
|
|
xi = xi*xscale2+xoffs2;
|
|
ae_assert(ae_fp_greater(xi,xprev), "BarycentricDiff1: points are too close!", _state);
|
|
xprev = xi;
|
|
if( i!=k )
|
|
{
|
|
vv = ae_sqr(t-xi, _state);
|
|
s0 = (t-xk)/(t-xi);
|
|
s1 = (xk-xi)/vv;
|
|
}
|
|
else
|
|
{
|
|
s0 = 1;
|
|
s1 = 0;
|
|
}
|
|
vv = b->w.ptr.p_double[i]*b->y.ptr.p_double[i];
|
|
n0 = n0+s0*vv;
|
|
n1 = n1+s1*vv;
|
|
vv = b->w.ptr.p_double[i];
|
|
d0 = d0+s0*vv;
|
|
d1 = d1+s1*vv;
|
|
}
|
|
*f = b->sy*n0/d0;
|
|
*df = (n1*d0-n0*d1)/ae_sqr(d0, _state);
|
|
if( ae_fp_neq(*df,0) )
|
|
{
|
|
*df = ae_sign(*df, _state)*ae_exp(ae_log(ae_fabs(*df, _state), _state)+ae_log(b->sy, _state)+ae_log(xscale1, _state)+ae_log(xscale2, _state), _state);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Differentiation of barycentric interpolant: first/second derivatives.
|
|
|
|
INPUT PARAMETERS:
|
|
B - barycentric interpolant built with one of model building
|
|
subroutines.
|
|
T - interpolation point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - barycentric interpolant at T
|
|
DF - first derivative
|
|
D2F - second derivative
|
|
|
|
NOTE: this algorithm may fail due to overflow/underflor if used on data
|
|
whose values are close to MaxRealNumber or MinRealNumber. Use more robust
|
|
BarycentricDiff1() subroutine in such cases.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricdiff2(barycentricinterpolant* b,
|
|
double t,
|
|
double* f,
|
|
double* df,
|
|
double* d2f,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
double vv;
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
double n0;
|
|
double n1;
|
|
double n2;
|
|
double d0;
|
|
double d1;
|
|
double d2;
|
|
double s0;
|
|
double s1;
|
|
double s2;
|
|
double xk;
|
|
double xi;
|
|
|
|
*f = 0;
|
|
*df = 0;
|
|
*d2f = 0;
|
|
|
|
ae_assert(!ae_isinf(t, _state), "BarycentricDiff1: infinite T!", _state);
|
|
|
|
/*
|
|
* special case: NaN
|
|
*/
|
|
if( ae_isnan(t, _state) )
|
|
{
|
|
*f = _state->v_nan;
|
|
*df = _state->v_nan;
|
|
*d2f = _state->v_nan;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* special case: N=1
|
|
*/
|
|
if( b->n==1 )
|
|
{
|
|
*f = b->sy*b->y.ptr.p_double[0];
|
|
*df = 0;
|
|
*d2f = 0;
|
|
return;
|
|
}
|
|
if( ae_fp_eq(b->sy,0) )
|
|
{
|
|
*f = 0;
|
|
*df = 0;
|
|
*d2f = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We assume than N>1 and B.SY>0. Find:
|
|
* 1. pivot point (X[i] closest to T)
|
|
* 2. width of interval containing X[i]
|
|
*/
|
|
ae_assert(ae_fp_greater(b->sy,0), "BarycentricDiff: internal error", _state);
|
|
*f = 0;
|
|
*df = 0;
|
|
*d2f = 0;
|
|
v = ae_fabs(b->x.ptr.p_double[0]-t, _state);
|
|
k = 0;
|
|
for(i=1; i<=b->n-1; i++)
|
|
{
|
|
vv = b->x.ptr.p_double[i];
|
|
if( ae_fp_less(ae_fabs(vv-t, _state),v) )
|
|
{
|
|
v = ae_fabs(vv-t, _state);
|
|
k = i;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* pivot point found, calculate dNumerator and dDenominator
|
|
*/
|
|
xk = b->x.ptr.p_double[k];
|
|
v = t-xk;
|
|
n0 = 0;
|
|
n1 = 0;
|
|
n2 = 0;
|
|
d0 = 0;
|
|
d1 = 0;
|
|
d2 = 0;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
if( i!=k )
|
|
{
|
|
xi = b->x.ptr.p_double[i];
|
|
vv = ae_sqr(t-xi, _state);
|
|
s0 = (t-xk)/(t-xi);
|
|
s1 = (xk-xi)/vv;
|
|
s2 = -2*(xk-xi)/(vv*(t-xi));
|
|
}
|
|
else
|
|
{
|
|
s0 = 1;
|
|
s1 = 0;
|
|
s2 = 0;
|
|
}
|
|
vv = b->w.ptr.p_double[i]*b->y.ptr.p_double[i];
|
|
n0 = n0+s0*vv;
|
|
n1 = n1+s1*vv;
|
|
n2 = n2+s2*vv;
|
|
vv = b->w.ptr.p_double[i];
|
|
d0 = d0+s0*vv;
|
|
d1 = d1+s1*vv;
|
|
d2 = d2+s2*vv;
|
|
}
|
|
*f = b->sy*n0/d0;
|
|
*df = b->sy*(n1*d0-n0*d1)/ae_sqr(d0, _state);
|
|
*d2f = b->sy*((n2*d0-n0*d2)*ae_sqr(d0, _state)-(n1*d0-n0*d1)*2*d0*d1)/ae_sqr(ae_sqr(d0, _state), _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the argument.
|
|
|
|
INPUT PARAMETERS:
|
|
B - rational interpolant in barycentric form
|
|
CA, CB - transformation coefficients: x = CA*t + CB
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - transformed interpolant with X replaced by T
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentriclintransx(barycentricinterpolant* b,
|
|
double ca,
|
|
double cb,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double v;
|
|
|
|
|
|
|
|
/*
|
|
* special case, replace by constant F(CB)
|
|
*/
|
|
if( ae_fp_eq(ca,0) )
|
|
{
|
|
b->sy = barycentriccalc(b, cb, _state);
|
|
v = 1;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
b->y.ptr.p_double[i] = 1;
|
|
b->w.ptr.p_double[i] = v;
|
|
v = -v;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* general case: CA<>0
|
|
*/
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
b->x.ptr.p_double[i] = (b->x.ptr.p_double[i]-cb)/ca;
|
|
}
|
|
if( ae_fp_less(ca,0) )
|
|
{
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
if( i<b->n-1-i )
|
|
{
|
|
j = b->n-1-i;
|
|
v = b->x.ptr.p_double[i];
|
|
b->x.ptr.p_double[i] = b->x.ptr.p_double[j];
|
|
b->x.ptr.p_double[j] = v;
|
|
v = b->y.ptr.p_double[i];
|
|
b->y.ptr.p_double[i] = b->y.ptr.p_double[j];
|
|
b->y.ptr.p_double[j] = v;
|
|
v = b->w.ptr.p_double[i];
|
|
b->w.ptr.p_double[i] = b->w.ptr.p_double[j];
|
|
b->w.ptr.p_double[j] = v;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the barycentric
|
|
interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
B - rational interpolant in barycentric form
|
|
CA, CB - transformation coefficients: B2(x) = CA*B(x) + CB
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - transformed interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentriclintransy(barycentricinterpolant* b,
|
|
double ca,
|
|
double cb,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
double v;
|
|
|
|
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
b->y.ptr.p_double[i] = ca*b->sy*b->y.ptr.p_double[i]+cb;
|
|
}
|
|
b->sy = 0;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
b->sy = ae_maxreal(b->sy, ae_fabs(b->y.ptr.p_double[i], _state), _state);
|
|
}
|
|
if( ae_fp_greater(b->sy,0) )
|
|
{
|
|
v = 1/b->sy;
|
|
ae_v_muld(&b->y.ptr.p_double[0], 1, ae_v_len(0,b->n-1), v);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Extracts X/Y/W arrays from rational interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
B - barycentric interpolant
|
|
|
|
OUTPUT PARAMETERS:
|
|
N - nodes count, N>0
|
|
X - interpolation nodes, array[0..N-1]
|
|
F - function values, array[0..N-1]
|
|
W - barycentric weights, array[0..N-1]
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricunpack(barycentricinterpolant* b,
|
|
ae_int_t* n,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
|
|
*n = 0;
|
|
ae_vector_clear(x);
|
|
ae_vector_clear(y);
|
|
ae_vector_clear(w);
|
|
|
|
*n = b->n;
|
|
ae_vector_set_length(x, *n, _state);
|
|
ae_vector_set_length(y, *n, _state);
|
|
ae_vector_set_length(w, *n, _state);
|
|
v = b->sy;
|
|
ae_v_move(&x->ptr.p_double[0], 1, &b->x.ptr.p_double[0], 1, ae_v_len(0,*n-1));
|
|
ae_v_moved(&y->ptr.p_double[0], 1, &b->y.ptr.p_double[0], 1, ae_v_len(0,*n-1), v);
|
|
ae_v_move(&w->ptr.p_double[0], 1, &b->w.ptr.p_double[0], 1, ae_v_len(0,*n-1));
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Rational interpolant from X/Y/W arrays
|
|
|
|
F(t) = SUM(i=0,n-1,w[i]*f[i]/(t-x[i])) / SUM(i=0,n-1,w[i]/(t-x[i]))
|
|
|
|
INPUT PARAMETERS:
|
|
X - interpolation nodes, array[0..N-1]
|
|
F - function values, array[0..N-1]
|
|
W - barycentric weights, array[0..N-1]
|
|
N - nodes count, N>0
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - barycentric interpolant built from (X, Y, W)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricbuildxyw(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
barycentricinterpolant* b,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_barycentricinterpolant_clear(b);
|
|
|
|
ae_assert(n>0, "BarycentricBuildXYW: incorrect N!", _state);
|
|
|
|
/*
|
|
* fill X/Y/W
|
|
*/
|
|
ae_vector_set_length(&b->x, n, _state);
|
|
ae_vector_set_length(&b->y, n, _state);
|
|
ae_vector_set_length(&b->w, n, _state);
|
|
ae_v_move(&b->x.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_v_move(&b->y.ptr.p_double[0], 1, &y->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_v_move(&b->w.ptr.p_double[0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
b->n = n;
|
|
|
|
/*
|
|
* Normalize
|
|
*/
|
|
ratint_barycentricnormalize(b, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Rational interpolant without poles
|
|
|
|
The subroutine constructs the rational interpolating function without real
|
|
poles (see 'Barycentric rational interpolation with no poles and high
|
|
rates of approximation', Michael S. Floater. and Kai Hormann, for more
|
|
information on this subject).
|
|
|
|
Input parameters:
|
|
X - interpolation nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of nodes, N>0.
|
|
D - order of the interpolation scheme, 0 <= D <= N-1.
|
|
D<0 will cause an error.
|
|
D>=N it will be replaced with D=N-1.
|
|
if you don't know what D to choose, use small value about 3-5.
|
|
|
|
Output parameters:
|
|
B - barycentric interpolant.
|
|
|
|
Note:
|
|
this algorithm always succeeds and calculates the weights with close
|
|
to machine precision.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 17.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricbuildfloaterhormann(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t d,
|
|
barycentricinterpolant* b,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
double s0;
|
|
double s;
|
|
double v;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_vector perm;
|
|
ae_vector wtemp;
|
|
ae_vector sortrbuf;
|
|
ae_vector sortrbuf2;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_barycentricinterpolant_clear(b);
|
|
ae_vector_init(&perm, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&wtemp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sortrbuf, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sortrbuf2, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>0, "BarycentricFloaterHormann: N<=0!", _state);
|
|
ae_assert(d>=0, "BarycentricFloaterHormann: incorrect D!", _state);
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
if( d>n-1 )
|
|
{
|
|
d = n-1;
|
|
}
|
|
b->n = n;
|
|
|
|
/*
|
|
* special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
ae_vector_set_length(&b->x, n, _state);
|
|
ae_vector_set_length(&b->y, n, _state);
|
|
ae_vector_set_length(&b->w, n, _state);
|
|
b->x.ptr.p_double[0] = x->ptr.p_double[0];
|
|
b->y.ptr.p_double[0] = y->ptr.p_double[0];
|
|
b->w.ptr.p_double[0] = 1;
|
|
ratint_barycentricnormalize(b, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Fill X/Y
|
|
*/
|
|
ae_vector_set_length(&b->x, n, _state);
|
|
ae_vector_set_length(&b->y, n, _state);
|
|
ae_v_move(&b->x.ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_v_move(&b->y.ptr.p_double[0], 1, &y->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
tagsortfastr(&b->x, &b->y, &sortrbuf, &sortrbuf2, n, _state);
|
|
|
|
/*
|
|
* Calculate Wk
|
|
*/
|
|
ae_vector_set_length(&b->w, n, _state);
|
|
s0 = 1;
|
|
for(k=1; k<=d; k++)
|
|
{
|
|
s0 = -s0;
|
|
}
|
|
for(k=0; k<=n-1; k++)
|
|
{
|
|
|
|
/*
|
|
* Wk
|
|
*/
|
|
s = 0;
|
|
for(i=ae_maxint(k-d, 0, _state); i<=ae_minint(k, n-1-d, _state); i++)
|
|
{
|
|
v = 1;
|
|
for(j=i; j<=i+d; j++)
|
|
{
|
|
if( j!=k )
|
|
{
|
|
v = v/ae_fabs(b->x.ptr.p_double[k]-b->x.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
s = s+v;
|
|
}
|
|
b->w.ptr.p_double[k] = s0*s;
|
|
|
|
/*
|
|
* Next S0
|
|
*/
|
|
s0 = -s0;
|
|
}
|
|
|
|
/*
|
|
* Normalize
|
|
*/
|
|
ratint_barycentricnormalize(b, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Copying of the barycentric interpolant (for internal use only)
|
|
|
|
INPUT PARAMETERS:
|
|
B - barycentric interpolant
|
|
|
|
OUTPUT PARAMETERS:
|
|
B2 - copy(B1)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentriccopy(barycentricinterpolant* b,
|
|
barycentricinterpolant* b2,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_barycentricinterpolant_clear(b2);
|
|
|
|
b2->n = b->n;
|
|
b2->sy = b->sy;
|
|
ae_vector_set_length(&b2->x, b2->n, _state);
|
|
ae_vector_set_length(&b2->y, b2->n, _state);
|
|
ae_vector_set_length(&b2->w, b2->n, _state);
|
|
ae_v_move(&b2->x.ptr.p_double[0], 1, &b->x.ptr.p_double[0], 1, ae_v_len(0,b2->n-1));
|
|
ae_v_move(&b2->y.ptr.p_double[0], 1, &b->y.ptr.p_double[0], 1, ae_v_len(0,b2->n-1));
|
|
ae_v_move(&b2->w.ptr.p_double[0], 1, &b->w.ptr.p_double[0], 1, ae_v_len(0,b2->n-1));
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Normalization of barycentric interpolant:
|
|
* B.N, B.X, B.Y and B.W are initialized
|
|
* B.SY is NOT initialized
|
|
* Y[] is normalized, scaling coefficient is stored in B.SY
|
|
* W[] is normalized, no scaling coefficient is stored
|
|
* X[] is sorted
|
|
|
|
Internal subroutine.
|
|
*************************************************************************/
|
|
static void ratint_barycentricnormalize(barycentricinterpolant* b,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector p1;
|
|
ae_vector p2;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t j2;
|
|
double v;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&p1, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Normalize task: |Y|<=1, |W|<=1, sort X[]
|
|
*/
|
|
b->sy = 0;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
b->sy = ae_maxreal(b->sy, ae_fabs(b->y.ptr.p_double[i], _state), _state);
|
|
}
|
|
if( ae_fp_greater(b->sy,0)&&ae_fp_greater(ae_fabs(b->sy-1, _state),10*ae_machineepsilon) )
|
|
{
|
|
v = 1/b->sy;
|
|
ae_v_muld(&b->y.ptr.p_double[0], 1, ae_v_len(0,b->n-1), v);
|
|
}
|
|
v = 0;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
v = ae_maxreal(v, ae_fabs(b->w.ptr.p_double[i], _state), _state);
|
|
}
|
|
if( ae_fp_greater(v,0)&&ae_fp_greater(ae_fabs(v-1, _state),10*ae_machineepsilon) )
|
|
{
|
|
v = 1/v;
|
|
ae_v_muld(&b->w.ptr.p_double[0], 1, ae_v_len(0,b->n-1), v);
|
|
}
|
|
for(i=0; i<=b->n-2; i++)
|
|
{
|
|
if( ae_fp_less(b->x.ptr.p_double[i+1],b->x.ptr.p_double[i]) )
|
|
{
|
|
tagsort(&b->x, b->n, &p1, &p2, _state);
|
|
for(j=0; j<=b->n-1; j++)
|
|
{
|
|
j2 = p2.ptr.p_int[j];
|
|
v = b->y.ptr.p_double[j];
|
|
b->y.ptr.p_double[j] = b->y.ptr.p_double[j2];
|
|
b->y.ptr.p_double[j2] = v;
|
|
v = b->w.ptr.p_double[j];
|
|
b->w.ptr.p_double[j] = b->w.ptr.p_double[j2];
|
|
b->w.ptr.p_double[j2] = v;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
ae_bool _barycentricinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
barycentricinterpolant *p = (barycentricinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->w, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _barycentricinterpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
barycentricinterpolant *dst = (barycentricinterpolant*)_dst;
|
|
barycentricinterpolant *src = (barycentricinterpolant*)_src;
|
|
dst->n = src->n;
|
|
dst->sy = src->sy;
|
|
if( !ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->w, &src->w, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _barycentricinterpolant_clear(void* _p)
|
|
{
|
|
barycentricinterpolant *p = (barycentricinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->x);
|
|
ae_vector_clear(&p->y);
|
|
ae_vector_clear(&p->w);
|
|
}
|
|
|
|
|
|
void _barycentricinterpolant_destroy(void* _p)
|
|
{
|
|
barycentricinterpolant *p = (barycentricinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->x);
|
|
ae_vector_destroy(&p->y);
|
|
ae_vector_destroy(&p->w);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
Conversion from barycentric representation to Chebyshev basis.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
P - polynomial in barycentric form
|
|
A,B - base interval for Chebyshev polynomials (see below)
|
|
A<>B
|
|
|
|
OUTPUT PARAMETERS
|
|
T - coefficients of Chebyshev representation;
|
|
P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N-1 },
|
|
where Ti - I-th Chebyshev polynomial.
|
|
|
|
NOTES:
|
|
barycentric interpolant passed as P may be either polynomial obtained
|
|
from polynomial interpolation/ fitting or rational function which is
|
|
NOT polynomial. We can't distinguish between these two cases, and this
|
|
algorithm just tries to work assuming that P IS a polynomial. If not,
|
|
algorithm will return results, but they won't have any meaning.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbar2cheb(barycentricinterpolant* p,
|
|
double a,
|
|
double b,
|
|
/* Real */ ae_vector* t,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
ae_vector vp;
|
|
ae_vector vx;
|
|
ae_vector tk;
|
|
ae_vector tk1;
|
|
double v;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_clear(t);
|
|
ae_vector_init(&vp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&vx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tk, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tk1, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialBar2Cheb: A is not finite!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialBar2Cheb: B is not finite!", _state);
|
|
ae_assert(ae_fp_neq(a,b), "PolynomialBar2Cheb: A=B!", _state);
|
|
ae_assert(p->n>0, "PolynomialBar2Cheb: P is not correctly initialized barycentric interpolant!", _state);
|
|
|
|
/*
|
|
* Calculate function values on a Chebyshev grid
|
|
*/
|
|
ae_vector_set_length(&vp, p->n, _state);
|
|
ae_vector_set_length(&vx, p->n, _state);
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
vx.ptr.p_double[i] = ae_cos(ae_pi*(i+0.5)/p->n, _state);
|
|
vp.ptr.p_double[i] = barycentriccalc(p, 0.5*(vx.ptr.p_double[i]+1)*(b-a)+a, _state);
|
|
}
|
|
|
|
/*
|
|
* T[0]
|
|
*/
|
|
ae_vector_set_length(t, p->n, _state);
|
|
v = 0;
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
v = v+vp.ptr.p_double[i];
|
|
}
|
|
t->ptr.p_double[0] = v/p->n;
|
|
|
|
/*
|
|
* other T's.
|
|
*
|
|
* NOTES:
|
|
* 1. TK stores T{k} on VX, TK1 stores T{k-1} on VX
|
|
* 2. we can do same calculations with fast DCT, but it
|
|
* * adds dependencies
|
|
* * still leaves us with O(N^2) algorithm because
|
|
* preparation of function values is O(N^2) process
|
|
*/
|
|
if( p->n>1 )
|
|
{
|
|
ae_vector_set_length(&tk, p->n, _state);
|
|
ae_vector_set_length(&tk1, p->n, _state);
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
tk.ptr.p_double[i] = vx.ptr.p_double[i];
|
|
tk1.ptr.p_double[i] = 1;
|
|
}
|
|
for(k=1; k<=p->n-1; k++)
|
|
{
|
|
|
|
/*
|
|
* calculate discrete product of function vector and TK
|
|
*/
|
|
v = ae_v_dotproduct(&tk.ptr.p_double[0], 1, &vp.ptr.p_double[0], 1, ae_v_len(0,p->n-1));
|
|
t->ptr.p_double[k] = v/(0.5*p->n);
|
|
|
|
/*
|
|
* Update TK and TK1
|
|
*/
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
v = 2*vx.ptr.p_double[i]*tk.ptr.p_double[i]-tk1.ptr.p_double[i];
|
|
tk1.ptr.p_double[i] = tk.ptr.p_double[i];
|
|
tk.ptr.p_double[i] = v;
|
|
}
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Conversion from Chebyshev basis to barycentric representation.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
T - coefficients of Chebyshev representation;
|
|
P(x) = sum { T[i]*Ti(2*(x-A)/(B-A)-1), i=0..N },
|
|
where Ti - I-th Chebyshev polynomial.
|
|
N - number of coefficients:
|
|
* if given, only leading N elements of T are used
|
|
* if not given, automatically determined from size of T
|
|
A,B - base interval for Chebyshev polynomials (see above)
|
|
A<B
|
|
|
|
OUTPUT PARAMETERS
|
|
P - polynomial in barycentric form
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialcheb2bar(/* Real */ ae_vector* t,
|
|
ae_int_t n,
|
|
double a,
|
|
double b,
|
|
barycentricinterpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
ae_vector y;
|
|
double tk;
|
|
double tk1;
|
|
double vx;
|
|
double vy;
|
|
double v;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_barycentricinterpolant_clear(p);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialBar2Cheb: A is not finite!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialBar2Cheb: B is not finite!", _state);
|
|
ae_assert(ae_fp_neq(a,b), "PolynomialBar2Cheb: A=B!", _state);
|
|
ae_assert(n>=1, "PolynomialBar2Cheb: N<1", _state);
|
|
ae_assert(t->cnt>=n, "PolynomialBar2Cheb: Length(T)<N", _state);
|
|
ae_assert(isfinitevector(t, n, _state), "PolynomialBar2Cheb: T[] contains INF or NAN", _state);
|
|
|
|
/*
|
|
* Calculate function values on a Chebyshev grid spanning [-1,+1]
|
|
*/
|
|
ae_vector_set_length(&y, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate value on a grid spanning [-1,+1]
|
|
*/
|
|
vx = ae_cos(ae_pi*(i+0.5)/n, _state);
|
|
vy = t->ptr.p_double[0];
|
|
tk1 = 1;
|
|
tk = vx;
|
|
for(k=1; k<=n-1; k++)
|
|
{
|
|
vy = vy+t->ptr.p_double[k]*tk;
|
|
v = 2*vx*tk-tk1;
|
|
tk1 = tk;
|
|
tk = v;
|
|
}
|
|
y.ptr.p_double[i] = vy;
|
|
}
|
|
|
|
/*
|
|
* Build barycentric interpolant, map grid from [-1,+1] to [A,B]
|
|
*/
|
|
polynomialbuildcheb1(a, b, &y, n, p, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Conversion from barycentric representation to power basis.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
P - polynomial in barycentric form
|
|
C - offset (see below); 0.0 is used as default value.
|
|
S - scale (see below); 1.0 is used as default value. S<>0.
|
|
|
|
OUTPUT PARAMETERS
|
|
A - coefficients, P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 }
|
|
N - number of coefficients (polynomial degree plus 1)
|
|
|
|
NOTES:
|
|
1. this function accepts offset and scale, which can be set to improve
|
|
numerical properties of polynomial. For example, if P was obtained as
|
|
result of interpolation on [-1,+1], you can set C=0 and S=1 and
|
|
represent P as sum of 1, x, x^2, x^3 and so on. In most cases you it
|
|
is exactly what you need.
|
|
|
|
However, if your interpolation model was built on [999,1001], you will
|
|
see significant growth of numerical errors when using {1, x, x^2, x^3}
|
|
as basis. Representing P as sum of 1, (x-1000), (x-1000)^2, (x-1000)^3
|
|
will be better option. Such representation can be obtained by using
|
|
1000.0 as offset C and 1.0 as scale S.
|
|
|
|
2. power basis is ill-conditioned and tricks described above can't solve
|
|
this problem completely. This function will return coefficients in
|
|
any case, but for N>8 they will become unreliable. However, N's
|
|
less than 5 are pretty safe.
|
|
|
|
3. barycentric interpolant passed as P may be either polynomial obtained
|
|
from polynomial interpolation/ fitting or rational function which is
|
|
NOT polynomial. We can't distinguish between these two cases, and this
|
|
algorithm just tries to work assuming that P IS a polynomial. If not,
|
|
algorithm will return results, but they won't have any meaning.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbar2pow(barycentricinterpolant* p,
|
|
double c,
|
|
double s,
|
|
/* Real */ ae_vector* a,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
double e;
|
|
double d;
|
|
ae_vector vp;
|
|
ae_vector vx;
|
|
ae_vector tk;
|
|
ae_vector tk1;
|
|
ae_vector t;
|
|
double v;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_clear(a);
|
|
ae_vector_init(&vp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&vx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tk, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tk1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&t, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(ae_isfinite(c, _state), "PolynomialBar2Pow: C is not finite!", _state);
|
|
ae_assert(ae_isfinite(s, _state), "PolynomialBar2Pow: S is not finite!", _state);
|
|
ae_assert(ae_fp_neq(s,0), "PolynomialBar2Pow: S=0!", _state);
|
|
ae_assert(p->n>0, "PolynomialBar2Pow: P is not correctly initialized barycentric interpolant!", _state);
|
|
|
|
/*
|
|
* Calculate function values on a Chebyshev grid
|
|
*/
|
|
ae_vector_set_length(&vp, p->n, _state);
|
|
ae_vector_set_length(&vx, p->n, _state);
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
vx.ptr.p_double[i] = ae_cos(ae_pi*(i+0.5)/p->n, _state);
|
|
vp.ptr.p_double[i] = barycentriccalc(p, s*vx.ptr.p_double[i]+c, _state);
|
|
}
|
|
|
|
/*
|
|
* T[0]
|
|
*/
|
|
ae_vector_set_length(&t, p->n, _state);
|
|
v = 0;
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
v = v+vp.ptr.p_double[i];
|
|
}
|
|
t.ptr.p_double[0] = v/p->n;
|
|
|
|
/*
|
|
* other T's.
|
|
*
|
|
* NOTES:
|
|
* 1. TK stores T{k} on VX, TK1 stores T{k-1} on VX
|
|
* 2. we can do same calculations with fast DCT, but it
|
|
* * adds dependencies
|
|
* * still leaves us with O(N^2) algorithm because
|
|
* preparation of function values is O(N^2) process
|
|
*/
|
|
if( p->n>1 )
|
|
{
|
|
ae_vector_set_length(&tk, p->n, _state);
|
|
ae_vector_set_length(&tk1, p->n, _state);
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
tk.ptr.p_double[i] = vx.ptr.p_double[i];
|
|
tk1.ptr.p_double[i] = 1;
|
|
}
|
|
for(k=1; k<=p->n-1; k++)
|
|
{
|
|
|
|
/*
|
|
* calculate discrete product of function vector and TK
|
|
*/
|
|
v = ae_v_dotproduct(&tk.ptr.p_double[0], 1, &vp.ptr.p_double[0], 1, ae_v_len(0,p->n-1));
|
|
t.ptr.p_double[k] = v/(0.5*p->n);
|
|
|
|
/*
|
|
* Update TK and TK1
|
|
*/
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
v = 2*vx.ptr.p_double[i]*tk.ptr.p_double[i]-tk1.ptr.p_double[i];
|
|
tk1.ptr.p_double[i] = tk.ptr.p_double[i];
|
|
tk.ptr.p_double[i] = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Convert from Chebyshev basis to power basis
|
|
*/
|
|
ae_vector_set_length(a, p->n, _state);
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
a->ptr.p_double[i] = 0;
|
|
}
|
|
d = 0;
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
for(k=i; k<=p->n-1; k++)
|
|
{
|
|
e = a->ptr.p_double[k];
|
|
a->ptr.p_double[k] = 0;
|
|
if( i<=1&&k==i )
|
|
{
|
|
a->ptr.p_double[k] = 1;
|
|
}
|
|
else
|
|
{
|
|
if( i!=0 )
|
|
{
|
|
a->ptr.p_double[k] = 2*d;
|
|
}
|
|
if( k>i+1 )
|
|
{
|
|
a->ptr.p_double[k] = a->ptr.p_double[k]-a->ptr.p_double[k-2];
|
|
}
|
|
}
|
|
d = e;
|
|
}
|
|
d = a->ptr.p_double[i];
|
|
e = 0;
|
|
k = i;
|
|
while(k<=p->n-1)
|
|
{
|
|
e = e+a->ptr.p_double[k]*t.ptr.p_double[k];
|
|
k = k+2;
|
|
}
|
|
a->ptr.p_double[i] = e;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Conversion from power basis to barycentric representation.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - coefficients, P(x) = sum { A[i]*((X-C)/S)^i, i=0..N-1 }
|
|
N - number of coefficients (polynomial degree plus 1)
|
|
* if given, only leading N elements of A are used
|
|
* if not given, automatically determined from size of A
|
|
C - offset (see below); 0.0 is used as default value.
|
|
S - scale (see below); 1.0 is used as default value. S<>0.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - polynomial in barycentric form
|
|
|
|
|
|
NOTES:
|
|
1. this function accepts offset and scale, which can be set to improve
|
|
numerical properties of polynomial. For example, if you interpolate on
|
|
[-1,+1], you can set C=0 and S=1 and convert from sum of 1, x, x^2,
|
|
x^3 and so on. In most cases you it is exactly what you need.
|
|
|
|
However, if your interpolation model was built on [999,1001], you will
|
|
see significant growth of numerical errors when using {1, x, x^2, x^3}
|
|
as input basis. Converting from sum of 1, (x-1000), (x-1000)^2,
|
|
(x-1000)^3 will be better option (you have to specify 1000.0 as offset
|
|
C and 1.0 as scale S).
|
|
|
|
2. power basis is ill-conditioned and tricks described above can't solve
|
|
this problem completely. This function will return barycentric model
|
|
in any case, but for N>8 accuracy well degrade. However, N's less than
|
|
5 are pretty safe.
|
|
|
|
-- ALGLIB --
|
|
Copyright 30.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialpow2bar(/* Real */ ae_vector* a,
|
|
ae_int_t n,
|
|
double c,
|
|
double s,
|
|
barycentricinterpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
ae_vector y;
|
|
double vx;
|
|
double vy;
|
|
double px;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_barycentricinterpolant_clear(p);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(ae_isfinite(c, _state), "PolynomialPow2Bar: C is not finite!", _state);
|
|
ae_assert(ae_isfinite(s, _state), "PolynomialPow2Bar: S is not finite!", _state);
|
|
ae_assert(ae_fp_neq(s,0), "PolynomialPow2Bar: S is zero!", _state);
|
|
ae_assert(n>=1, "PolynomialPow2Bar: N<1", _state);
|
|
ae_assert(a->cnt>=n, "PolynomialPow2Bar: Length(A)<N", _state);
|
|
ae_assert(isfinitevector(a, n, _state), "PolynomialPow2Bar: A[] contains INF or NAN", _state);
|
|
|
|
/*
|
|
* Calculate function values on a Chebyshev grid spanning [-1,+1]
|
|
*/
|
|
ae_vector_set_length(&y, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate value on a grid spanning [-1,+1]
|
|
*/
|
|
vx = ae_cos(ae_pi*(i+0.5)/n, _state);
|
|
vy = a->ptr.p_double[0];
|
|
px = vx;
|
|
for(k=1; k<=n-1; k++)
|
|
{
|
|
vy = vy+px*a->ptr.p_double[k];
|
|
px = px*vx;
|
|
}
|
|
y.ptr.p_double[i] = vy;
|
|
}
|
|
|
|
/*
|
|
* Build barycentric interpolant, map grid from [-1,+1] to [A,B]
|
|
*/
|
|
polynomialbuildcheb1(c-s, c+s, &y, n, p, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant: generation of the model on the general grid.
|
|
This function has O(N^2) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
X - abscissas, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - number of points, N>=1
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuild(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
barycentricinterpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_vector w;
|
|
double b;
|
|
double a;
|
|
double v;
|
|
double mx;
|
|
ae_vector sortrbuf;
|
|
ae_vector sortrbuf2;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
_barycentricinterpolant_clear(p);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sortrbuf, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sortrbuf2, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>0, "PolynomialBuild: N<=0!", _state);
|
|
ae_assert(x->cnt>=n, "PolynomialBuild: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "PolynomialBuild: Length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "PolynomialBuild: X contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "PolynomialBuild: Y contains infinite or NaN values!", _state);
|
|
tagsortfastr(x, y, &sortrbuf, &sortrbuf2, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "PolynomialBuild: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* calculate W[j]
|
|
* multi-pass algorithm is used to avoid overflow
|
|
*/
|
|
ae_vector_set_length(&w, n, _state);
|
|
a = x->ptr.p_double[0];
|
|
b = x->ptr.p_double[0];
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
w.ptr.p_double[j] = 1;
|
|
a = ae_minreal(a, x->ptr.p_double[j], _state);
|
|
b = ae_maxreal(b, x->ptr.p_double[j], _state);
|
|
}
|
|
for(k=0; k<=n-1; k++)
|
|
{
|
|
|
|
/*
|
|
* W[K] is used instead of 0.0 because
|
|
* cycle on J does not touch K-th element
|
|
* and we MUST get maximum from ALL elements
|
|
*/
|
|
mx = ae_fabs(w.ptr.p_double[k], _state);
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
if( j!=k )
|
|
{
|
|
v = (b-a)/(x->ptr.p_double[j]-x->ptr.p_double[k]);
|
|
w.ptr.p_double[j] = w.ptr.p_double[j]*v;
|
|
mx = ae_maxreal(mx, ae_fabs(w.ptr.p_double[j], _state), _state);
|
|
}
|
|
}
|
|
if( k%5==0 )
|
|
{
|
|
|
|
/*
|
|
* every 5-th run we renormalize W[]
|
|
*/
|
|
v = 1/mx;
|
|
ae_v_muld(&w.ptr.p_double[0], 1, ae_v_len(0,n-1), v);
|
|
}
|
|
}
|
|
barycentricbuildxyw(x, y, &w, n, p, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant: generation of the model on equidistant grid.
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1]
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildeqdist(double a,
|
|
double b,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
barycentricinterpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector w;
|
|
ae_vector x;
|
|
double v;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_barycentricinterpolant_clear(p);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>0, "PolynomialBuildEqDist: N<=0!", _state);
|
|
ae_assert(y->cnt>=n, "PolynomialBuildEqDist: Length(Y)<N!", _state);
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialBuildEqDist: A is infinite or NaN!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialBuildEqDist: B is infinite or NaN!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "PolynomialBuildEqDist: Y contains infinite or NaN values!", _state);
|
|
ae_assert(ae_fp_neq(b,a), "PolynomialBuildEqDist: B=A!", _state);
|
|
ae_assert(ae_fp_neq(a+(b-a)/n,a), "PolynomialBuildEqDist: B is too close to A!", _state);
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
ae_vector_set_length(&x, 1, _state);
|
|
ae_vector_set_length(&w, 1, _state);
|
|
x.ptr.p_double[0] = 0.5*(b+a);
|
|
w.ptr.p_double[0] = 1;
|
|
barycentricbuildxyw(&x, y, &w, 1, p, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* general case
|
|
*/
|
|
ae_vector_set_length(&x, n, _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
v = 1;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = v;
|
|
x.ptr.p_double[i] = a+(b-a)*i/(n-1);
|
|
v = -v*(n-1-i);
|
|
v = v/(i+1);
|
|
}
|
|
barycentricbuildxyw(&x, y, &w, n, p, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant on Chebyshev grid (first kind).
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1],
|
|
Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n)))
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildcheb1(double a,
|
|
double b,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
barycentricinterpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector w;
|
|
ae_vector x;
|
|
double v;
|
|
double t;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_barycentricinterpolant_clear(p);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>0, "PolynomialBuildCheb1: N<=0!", _state);
|
|
ae_assert(y->cnt>=n, "PolynomialBuildCheb1: Length(Y)<N!", _state);
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialBuildCheb1: A is infinite or NaN!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialBuildCheb1: B is infinite or NaN!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "PolynomialBuildCheb1: Y contains infinite or NaN values!", _state);
|
|
ae_assert(ae_fp_neq(b,a), "PolynomialBuildCheb1: B=A!", _state);
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
ae_vector_set_length(&x, 1, _state);
|
|
ae_vector_set_length(&w, 1, _state);
|
|
x.ptr.p_double[0] = 0.5*(b+a);
|
|
w.ptr.p_double[0] = 1;
|
|
barycentricbuildxyw(&x, y, &w, 1, p, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* general case
|
|
*/
|
|
ae_vector_set_length(&x, n, _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
v = 1;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
t = ae_tan(0.5*ae_pi*(2*i+1)/(2*n), _state);
|
|
w.ptr.p_double[i] = 2*v*t/(1+ae_sqr(t, _state));
|
|
x.ptr.p_double[i] = 0.5*(b+a)+0.5*(b-a)*(1-ae_sqr(t, _state))/(1+ae_sqr(t, _state));
|
|
v = -v;
|
|
}
|
|
barycentricbuildxyw(&x, y, &w, n, p, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Lagrange intepolant on Chebyshev grid (second kind).
|
|
This function has O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
Y - function values at the nodes, array[0..N-1],
|
|
Y[I] = Y(0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1)))
|
|
N - number of points, N>=1
|
|
for N=1 a constant model is constructed.
|
|
|
|
OUTPUT PARAMETERS
|
|
P - barycentric model which represents Lagrange interpolant
|
|
(see ratint unit info and BarycentricCalc() description for
|
|
more information).
|
|
|
|
-- ALGLIB --
|
|
Copyright 03.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialbuildcheb2(double a,
|
|
double b,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
barycentricinterpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector w;
|
|
ae_vector x;
|
|
double v;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_barycentricinterpolant_clear(p);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>0, "PolynomialBuildCheb2: N<=0!", _state);
|
|
ae_assert(y->cnt>=n, "PolynomialBuildCheb2: Length(Y)<N!", _state);
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialBuildCheb2: A is infinite or NaN!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialBuildCheb2: B is infinite or NaN!", _state);
|
|
ae_assert(ae_fp_neq(b,a), "PolynomialBuildCheb2: B=A!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "PolynomialBuildCheb2: Y contains infinite or NaN values!", _state);
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
ae_vector_set_length(&x, 1, _state);
|
|
ae_vector_set_length(&w, 1, _state);
|
|
x.ptr.p_double[0] = 0.5*(b+a);
|
|
w.ptr.p_double[0] = 1;
|
|
barycentricbuildxyw(&x, y, &w, 1, p, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* general case
|
|
*/
|
|
ae_vector_set_length(&x, n, _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
v = 1;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( i==0||i==n-1 )
|
|
{
|
|
w.ptr.p_double[i] = v*0.5;
|
|
}
|
|
else
|
|
{
|
|
w.ptr.p_double[i] = v;
|
|
}
|
|
x.ptr.p_double[i] = 0.5*(b+a)+0.5*(b-a)*ae_cos(ae_pi*i/(n-1), _state);
|
|
v = -v;
|
|
}
|
|
barycentricbuildxyw(&x, y, &w, n, p, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fast equidistant polynomial interpolation function with O(N) complexity
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on equidistant grid, N>=1
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolynomialBuildEqDist()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalceqdist(double a,
|
|
double b,
|
|
/* Real */ ae_vector* f,
|
|
ae_int_t n,
|
|
double t,
|
|
ae_state *_state)
|
|
{
|
|
double s1;
|
|
double s2;
|
|
double v;
|
|
double threshold;
|
|
double s;
|
|
double h;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double w;
|
|
double x;
|
|
double result;
|
|
|
|
|
|
ae_assert(n>0, "PolynomialCalcEqDist: N<=0!", _state);
|
|
ae_assert(f->cnt>=n, "PolynomialCalcEqDist: Length(F)<N!", _state);
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialCalcEqDist: A is infinite or NaN!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialCalcEqDist: B is infinite or NaN!", _state);
|
|
ae_assert(isfinitevector(f, n, _state), "PolynomialCalcEqDist: F contains infinite or NaN values!", _state);
|
|
ae_assert(ae_fp_neq(b,a), "PolynomialCalcEqDist: B=A!", _state);
|
|
ae_assert(!ae_isinf(t, _state), "PolynomialCalcEqDist: T is infinite!", _state);
|
|
|
|
/*
|
|
* Special case: T is NAN
|
|
*/
|
|
if( ae_isnan(t, _state) )
|
|
{
|
|
result = _state->v_nan;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
result = f->ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* First, decide: should we use "safe" formula (guarded
|
|
* against overflow) or fast one?
|
|
*/
|
|
threshold = ae_sqrt(ae_minrealnumber, _state);
|
|
j = 0;
|
|
s = t-a;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
x = a+(double)i/(double)(n-1)*(b-a);
|
|
if( ae_fp_less(ae_fabs(t-x, _state),ae_fabs(s, _state)) )
|
|
{
|
|
s = t-x;
|
|
j = i;
|
|
}
|
|
}
|
|
if( ae_fp_eq(s,0) )
|
|
{
|
|
result = f->ptr.p_double[j];
|
|
return result;
|
|
}
|
|
if( ae_fp_greater(ae_fabs(s, _state),threshold) )
|
|
{
|
|
|
|
/*
|
|
* use fast formula
|
|
*/
|
|
j = -1;
|
|
s = 1.0;
|
|
}
|
|
|
|
/*
|
|
* Calculate using safe or fast barycentric formula
|
|
*/
|
|
s1 = 0;
|
|
s2 = 0;
|
|
w = 1.0;
|
|
h = (b-a)/(n-1);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( i!=j )
|
|
{
|
|
v = s*w/(t-(a+i*h));
|
|
s1 = s1+v*f->ptr.p_double[i];
|
|
s2 = s2+v;
|
|
}
|
|
else
|
|
{
|
|
v = w;
|
|
s1 = s1+v*f->ptr.p_double[i];
|
|
s2 = s2+v;
|
|
}
|
|
w = -w*(n-1-i);
|
|
w = w/(i+1);
|
|
}
|
|
result = s1/s2;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fast polynomial interpolation function on Chebyshev points (first kind)
|
|
with O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on Chebyshev grid (first kind),
|
|
X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*(2*i+1)/(2*n))
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolIntBuildCheb1()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalccheb1(double a,
|
|
double b,
|
|
/* Real */ ae_vector* f,
|
|
ae_int_t n,
|
|
double t,
|
|
ae_state *_state)
|
|
{
|
|
double s1;
|
|
double s2;
|
|
double v;
|
|
double threshold;
|
|
double s;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double a0;
|
|
double delta;
|
|
double alpha;
|
|
double beta;
|
|
double ca;
|
|
double sa;
|
|
double tempc;
|
|
double temps;
|
|
double x;
|
|
double w;
|
|
double p1;
|
|
double result;
|
|
|
|
|
|
ae_assert(n>0, "PolynomialCalcCheb1: N<=0!", _state);
|
|
ae_assert(f->cnt>=n, "PolynomialCalcCheb1: Length(F)<N!", _state);
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialCalcCheb1: A is infinite or NaN!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialCalcCheb1: B is infinite or NaN!", _state);
|
|
ae_assert(isfinitevector(f, n, _state), "PolynomialCalcCheb1: F contains infinite or NaN values!", _state);
|
|
ae_assert(ae_fp_neq(b,a), "PolynomialCalcCheb1: B=A!", _state);
|
|
ae_assert(!ae_isinf(t, _state), "PolynomialCalcCheb1: T is infinite!", _state);
|
|
|
|
/*
|
|
* Special case: T is NAN
|
|
*/
|
|
if( ae_isnan(t, _state) )
|
|
{
|
|
result = _state->v_nan;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
result = f->ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Prepare information for the recurrence formula
|
|
* used to calculate sin(pi*(2j+1)/(2n+2)) and
|
|
* cos(pi*(2j+1)/(2n+2)):
|
|
*
|
|
* A0 = pi/(2n+2)
|
|
* Delta = pi/(n+1)
|
|
* Alpha = 2 sin^2 (Delta/2)
|
|
* Beta = sin(Delta)
|
|
*
|
|
* so that sin(..) = sin(A0+j*delta) and cos(..) = cos(A0+j*delta).
|
|
* Then we use
|
|
*
|
|
* sin(x+delta) = sin(x) - (alpha*sin(x) - beta*cos(x))
|
|
* cos(x+delta) = cos(x) - (alpha*cos(x) - beta*sin(x))
|
|
*
|
|
* to repeatedly calculate sin(..) and cos(..).
|
|
*/
|
|
threshold = ae_sqrt(ae_minrealnumber, _state);
|
|
t = (t-0.5*(a+b))/(0.5*(b-a));
|
|
a0 = ae_pi/(2*(n-1)+2);
|
|
delta = 2*ae_pi/(2*(n-1)+2);
|
|
alpha = 2*ae_sqr(ae_sin(delta/2, _state), _state);
|
|
beta = ae_sin(delta, _state);
|
|
|
|
/*
|
|
* First, decide: should we use "safe" formula (guarded
|
|
* against overflow) or fast one?
|
|
*/
|
|
ca = ae_cos(a0, _state);
|
|
sa = ae_sin(a0, _state);
|
|
j = 0;
|
|
x = ca;
|
|
s = t-x;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Next X[i]
|
|
*/
|
|
temps = sa-(alpha*sa-beta*ca);
|
|
tempc = ca-(alpha*ca+beta*sa);
|
|
sa = temps;
|
|
ca = tempc;
|
|
x = ca;
|
|
|
|
/*
|
|
* Use X[i]
|
|
*/
|
|
if( ae_fp_less(ae_fabs(t-x, _state),ae_fabs(s, _state)) )
|
|
{
|
|
s = t-x;
|
|
j = i;
|
|
}
|
|
}
|
|
if( ae_fp_eq(s,0) )
|
|
{
|
|
result = f->ptr.p_double[j];
|
|
return result;
|
|
}
|
|
if( ae_fp_greater(ae_fabs(s, _state),threshold) )
|
|
{
|
|
|
|
/*
|
|
* use fast formula
|
|
*/
|
|
j = -1;
|
|
s = 1.0;
|
|
}
|
|
|
|
/*
|
|
* Calculate using safe or fast barycentric formula
|
|
*/
|
|
s1 = 0;
|
|
s2 = 0;
|
|
ca = ae_cos(a0, _state);
|
|
sa = ae_sin(a0, _state);
|
|
p1 = 1.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate X[i], W[i]
|
|
*/
|
|
x = ca;
|
|
w = p1*sa;
|
|
|
|
/*
|
|
* Proceed
|
|
*/
|
|
if( i!=j )
|
|
{
|
|
v = s*w/(t-x);
|
|
s1 = s1+v*f->ptr.p_double[i];
|
|
s2 = s2+v;
|
|
}
|
|
else
|
|
{
|
|
v = w;
|
|
s1 = s1+v*f->ptr.p_double[i];
|
|
s2 = s2+v;
|
|
}
|
|
|
|
/*
|
|
* Next CA, SA, P1
|
|
*/
|
|
temps = sa-(alpha*sa-beta*ca);
|
|
tempc = ca-(alpha*ca+beta*sa);
|
|
sa = temps;
|
|
ca = tempc;
|
|
p1 = -p1;
|
|
}
|
|
result = s1/s2;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fast polynomial interpolation function on Chebyshev points (second kind)
|
|
with O(N) complexity.
|
|
|
|
INPUT PARAMETERS:
|
|
A - left boundary of [A,B]
|
|
B - right boundary of [A,B]
|
|
F - function values, array[0..N-1]
|
|
N - number of points on Chebyshev grid (second kind),
|
|
X[i] = 0.5*(B+A) + 0.5*(B-A)*Cos(PI*i/(n-1))
|
|
for N=1 a constant model is constructed.
|
|
T - position where P(x) is calculated
|
|
|
|
RESULT
|
|
value of the Lagrange interpolant at T
|
|
|
|
IMPORTANT
|
|
this function provides fast interface which is not overflow-safe
|
|
nor it is very precise.
|
|
the best option is to use PolIntBuildCheb2()/BarycentricCalc()
|
|
subroutines unless you are pretty sure that your data will not result
|
|
in overflow.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double polynomialcalccheb2(double a,
|
|
double b,
|
|
/* Real */ ae_vector* f,
|
|
ae_int_t n,
|
|
double t,
|
|
ae_state *_state)
|
|
{
|
|
double s1;
|
|
double s2;
|
|
double v;
|
|
double threshold;
|
|
double s;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double a0;
|
|
double delta;
|
|
double alpha;
|
|
double beta;
|
|
double ca;
|
|
double sa;
|
|
double tempc;
|
|
double temps;
|
|
double x;
|
|
double w;
|
|
double p1;
|
|
double result;
|
|
|
|
|
|
ae_assert(n>0, "PolynomialCalcCheb2: N<=0!", _state);
|
|
ae_assert(f->cnt>=n, "PolynomialCalcCheb2: Length(F)<N!", _state);
|
|
ae_assert(ae_isfinite(a, _state), "PolynomialCalcCheb2: A is infinite or NaN!", _state);
|
|
ae_assert(ae_isfinite(b, _state), "PolynomialCalcCheb2: B is infinite or NaN!", _state);
|
|
ae_assert(ae_fp_neq(b,a), "PolynomialCalcCheb2: B=A!", _state);
|
|
ae_assert(isfinitevector(f, n, _state), "PolynomialCalcCheb2: F contains infinite or NaN values!", _state);
|
|
ae_assert(!ae_isinf(t, _state), "PolynomialCalcEqDist: T is infinite!", _state);
|
|
|
|
/*
|
|
* Special case: T is NAN
|
|
*/
|
|
if( ae_isnan(t, _state) )
|
|
{
|
|
result = _state->v_nan;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Special case: N=1
|
|
*/
|
|
if( n==1 )
|
|
{
|
|
result = f->ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Prepare information for the recurrence formula
|
|
* used to calculate sin(pi*i/n) and
|
|
* cos(pi*i/n):
|
|
*
|
|
* A0 = 0
|
|
* Delta = pi/n
|
|
* Alpha = 2 sin^2 (Delta/2)
|
|
* Beta = sin(Delta)
|
|
*
|
|
* so that sin(..) = sin(A0+j*delta) and cos(..) = cos(A0+j*delta).
|
|
* Then we use
|
|
*
|
|
* sin(x+delta) = sin(x) - (alpha*sin(x) - beta*cos(x))
|
|
* cos(x+delta) = cos(x) - (alpha*cos(x) - beta*sin(x))
|
|
*
|
|
* to repeatedly calculate sin(..) and cos(..).
|
|
*/
|
|
threshold = ae_sqrt(ae_minrealnumber, _state);
|
|
t = (t-0.5*(a+b))/(0.5*(b-a));
|
|
a0 = 0.0;
|
|
delta = ae_pi/(n-1);
|
|
alpha = 2*ae_sqr(ae_sin(delta/2, _state), _state);
|
|
beta = ae_sin(delta, _state);
|
|
|
|
/*
|
|
* First, decide: should we use "safe" formula (guarded
|
|
* against overflow) or fast one?
|
|
*/
|
|
ca = ae_cos(a0, _state);
|
|
sa = ae_sin(a0, _state);
|
|
j = 0;
|
|
x = ca;
|
|
s = t-x;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Next X[i]
|
|
*/
|
|
temps = sa-(alpha*sa-beta*ca);
|
|
tempc = ca-(alpha*ca+beta*sa);
|
|
sa = temps;
|
|
ca = tempc;
|
|
x = ca;
|
|
|
|
/*
|
|
* Use X[i]
|
|
*/
|
|
if( ae_fp_less(ae_fabs(t-x, _state),ae_fabs(s, _state)) )
|
|
{
|
|
s = t-x;
|
|
j = i;
|
|
}
|
|
}
|
|
if( ae_fp_eq(s,0) )
|
|
{
|
|
result = f->ptr.p_double[j];
|
|
return result;
|
|
}
|
|
if( ae_fp_greater(ae_fabs(s, _state),threshold) )
|
|
{
|
|
|
|
/*
|
|
* use fast formula
|
|
*/
|
|
j = -1;
|
|
s = 1.0;
|
|
}
|
|
|
|
/*
|
|
* Calculate using safe or fast barycentric formula
|
|
*/
|
|
s1 = 0;
|
|
s2 = 0;
|
|
ca = ae_cos(a0, _state);
|
|
sa = ae_sin(a0, _state);
|
|
p1 = 1.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate X[i], W[i]
|
|
*/
|
|
x = ca;
|
|
if( i==0||i==n-1 )
|
|
{
|
|
w = 0.5*p1;
|
|
}
|
|
else
|
|
{
|
|
w = 1.0*p1;
|
|
}
|
|
|
|
/*
|
|
* Proceed
|
|
*/
|
|
if( i!=j )
|
|
{
|
|
v = s*w/(t-x);
|
|
s1 = s1+v*f->ptr.p_double[i];
|
|
s2 = s2+v;
|
|
}
|
|
else
|
|
{
|
|
v = w;
|
|
s1 = s1+v*f->ptr.p_double[i];
|
|
s2 = s2+v;
|
|
}
|
|
|
|
/*
|
|
* Next CA, SA, P1
|
|
*/
|
|
temps = sa-(alpha*sa-beta*ca);
|
|
tempc = ca-(alpha*ca+beta*sa);
|
|
sa = temps;
|
|
ca = tempc;
|
|
p1 = -p1;
|
|
}
|
|
result = s1/s2;
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds linear spline interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildlinear(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
spline1dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
_spline1dinterpolant_clear(c);
|
|
|
|
ae_assert(n>1, "Spline1DBuildLinear: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DBuildLinear: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DBuildLinear: Length(Y)<N!", _state);
|
|
|
|
/*
|
|
* check and sort points
|
|
*/
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DBuildLinear: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DBuildLinear: Y contains infinite or NAN values!", _state);
|
|
spline1d_heapsortpoints(x, y, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DBuildLinear: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Build
|
|
*/
|
|
c->periodic = ae_false;
|
|
c->n = n;
|
|
c->k = 3;
|
|
c->continuity = 0;
|
|
ae_vector_set_length(&c->x, n, _state);
|
|
ae_vector_set_length(&c->c, 4*(n-1)+2, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
c->x.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
c->c.ptr.p_double[4*i+0] = y->ptr.p_double[i];
|
|
c->c.ptr.p_double[4*i+1] = (y->ptr.p_double[i+1]-y->ptr.p_double[i])/(x->ptr.p_double[i+1]-x->ptr.p_double[i]);
|
|
c->c.ptr.p_double[4*i+2] = 0;
|
|
c->c.ptr.p_double[4*i+3] = 0;
|
|
}
|
|
c->c.ptr.p_double[4*(n-1)+0] = y->ptr.p_double[n-1];
|
|
c->c.ptr.p_double[4*(n-1)+1] = c->c.ptr.p_double[4*(n-2)+1];
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds cubic spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildcubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
spline1dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector a1;
|
|
ae_vector a2;
|
|
ae_vector a3;
|
|
ae_vector b;
|
|
ae_vector dt;
|
|
ae_vector d;
|
|
ae_vector p;
|
|
ae_int_t ylen;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
_spline1dinterpolant_clear(c);
|
|
ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dt, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* check correctness of boundary conditions
|
|
*/
|
|
ae_assert(((boundltype==-1||boundltype==0)||boundltype==1)||boundltype==2, "Spline1DBuildCubic: incorrect BoundLType!", _state);
|
|
ae_assert(((boundrtype==-1||boundrtype==0)||boundrtype==1)||boundrtype==2, "Spline1DBuildCubic: incorrect BoundRType!", _state);
|
|
ae_assert((boundrtype==-1&&boundltype==-1)||(boundrtype!=-1&&boundltype!=-1), "Spline1DBuildCubic: incorrect BoundLType/BoundRType!", _state);
|
|
if( boundltype==1||boundltype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundl, _state), "Spline1DBuildCubic: BoundL is infinite or NAN!", _state);
|
|
}
|
|
if( boundrtype==1||boundrtype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundr, _state), "Spline1DBuildCubic: BoundR is infinite or NAN!", _state);
|
|
}
|
|
|
|
/*
|
|
* check lengths of arguments
|
|
*/
|
|
ae_assert(n>=2, "Spline1DBuildCubic: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DBuildCubic: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DBuildCubic: Length(Y)<N!", _state);
|
|
|
|
/*
|
|
* check and sort points
|
|
*/
|
|
ylen = n;
|
|
if( boundltype==-1 )
|
|
{
|
|
ylen = n-1;
|
|
}
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DBuildCubic: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, ylen, _state), "Spline1DBuildCubic: Y contains infinite or NAN values!", _state);
|
|
spline1d_heapsortppoints(x, y, &p, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DBuildCubic: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Now we've checked and preordered everything,
|
|
* so we can call internal function to calculate derivatives,
|
|
* and then build Hermite spline using these derivatives
|
|
*/
|
|
if( boundltype==-1||boundrtype==-1 )
|
|
{
|
|
y->ptr.p_double[n-1] = y->ptr.p_double[0];
|
|
}
|
|
spline1d_spline1dgriddiffcubicinternal(x, y, n, boundltype, boundl, boundrtype, boundr, &d, &a1, &a2, &a3, &b, &dt, _state);
|
|
spline1dbuildhermite(x, y, &d, n, c, _state);
|
|
c->periodic = boundltype==-1||boundrtype==-1;
|
|
c->continuity = 2;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at nodes x[], it calculates and returns table of function derivatives d[]
|
|
(calculated at the same nodes x[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes
|
|
Y - function values
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
D - derivative values at X[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Derivative values are correctly reordered on return, so D[I] is always
|
|
equal to S'(X[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dgriddiffcubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
/* Real */ ae_vector* d,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector a1;
|
|
ae_vector a2;
|
|
ae_vector a3;
|
|
ae_vector b;
|
|
ae_vector dt;
|
|
ae_vector p;
|
|
ae_int_t i;
|
|
ae_int_t ylen;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_clear(d);
|
|
ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dt, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* check correctness of boundary conditions
|
|
*/
|
|
ae_assert(((boundltype==-1||boundltype==0)||boundltype==1)||boundltype==2, "Spline1DGridDiffCubic: incorrect BoundLType!", _state);
|
|
ae_assert(((boundrtype==-1||boundrtype==0)||boundrtype==1)||boundrtype==2, "Spline1DGridDiffCubic: incorrect BoundRType!", _state);
|
|
ae_assert((boundrtype==-1&&boundltype==-1)||(boundrtype!=-1&&boundltype!=-1), "Spline1DGridDiffCubic: incorrect BoundLType/BoundRType!", _state);
|
|
if( boundltype==1||boundltype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundl, _state), "Spline1DGridDiffCubic: BoundL is infinite or NAN!", _state);
|
|
}
|
|
if( boundrtype==1||boundrtype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundr, _state), "Spline1DGridDiffCubic: BoundR is infinite or NAN!", _state);
|
|
}
|
|
|
|
/*
|
|
* check lengths of arguments
|
|
*/
|
|
ae_assert(n>=2, "Spline1DGridDiffCubic: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DGridDiffCubic: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DGridDiffCubic: Length(Y)<N!", _state);
|
|
|
|
/*
|
|
* check and sort points
|
|
*/
|
|
ylen = n;
|
|
if( boundltype==-1 )
|
|
{
|
|
ylen = n-1;
|
|
}
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DGridDiffCubic: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, ylen, _state), "Spline1DGridDiffCubic: Y contains infinite or NAN values!", _state);
|
|
spline1d_heapsortppoints(x, y, &p, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DGridDiffCubic: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Now we've checked and preordered everything,
|
|
* so we can call internal function.
|
|
*/
|
|
spline1d_spline1dgriddiffcubicinternal(x, y, n, boundltype, boundl, boundrtype, boundr, d, &a1, &a2, &a3, &b, &dt, _state);
|
|
|
|
/*
|
|
* Remember that HeapSortPPoints() call?
|
|
* Now we have to reorder them back.
|
|
*/
|
|
if( dt.cnt<n )
|
|
{
|
|
ae_vector_set_length(&dt, n, _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
dt.ptr.p_double[p.ptr.p_int[i]] = d->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&d->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at nodes x[], it calculates and returns tables of first and second
|
|
function derivatives d1[] and d2[] (calculated at the same nodes x[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes
|
|
Y - function values
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
|
|
OUTPUT PARAMETERS:
|
|
D1 - S' values at X[]
|
|
D2 - S'' values at X[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Derivative values are correctly reordered on return, so D[I] is always
|
|
equal to S'(X[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dgriddiff2cubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
/* Real */ ae_vector* d1,
|
|
/* Real */ ae_vector* d2,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector a1;
|
|
ae_vector a2;
|
|
ae_vector a3;
|
|
ae_vector b;
|
|
ae_vector dt;
|
|
ae_vector p;
|
|
ae_int_t i;
|
|
ae_int_t ylen;
|
|
double delta;
|
|
double delta2;
|
|
double delta3;
|
|
double s2;
|
|
double s3;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_clear(d1);
|
|
ae_vector_clear(d2);
|
|
ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dt, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* check correctness of boundary conditions
|
|
*/
|
|
ae_assert(((boundltype==-1||boundltype==0)||boundltype==1)||boundltype==2, "Spline1DGridDiff2Cubic: incorrect BoundLType!", _state);
|
|
ae_assert(((boundrtype==-1||boundrtype==0)||boundrtype==1)||boundrtype==2, "Spline1DGridDiff2Cubic: incorrect BoundRType!", _state);
|
|
ae_assert((boundrtype==-1&&boundltype==-1)||(boundrtype!=-1&&boundltype!=-1), "Spline1DGridDiff2Cubic: incorrect BoundLType/BoundRType!", _state);
|
|
if( boundltype==1||boundltype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundl, _state), "Spline1DGridDiff2Cubic: BoundL is infinite or NAN!", _state);
|
|
}
|
|
if( boundrtype==1||boundrtype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundr, _state), "Spline1DGridDiff2Cubic: BoundR is infinite or NAN!", _state);
|
|
}
|
|
|
|
/*
|
|
* check lengths of arguments
|
|
*/
|
|
ae_assert(n>=2, "Spline1DGridDiff2Cubic: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DGridDiff2Cubic: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DGridDiff2Cubic: Length(Y)<N!", _state);
|
|
|
|
/*
|
|
* check and sort points
|
|
*/
|
|
ylen = n;
|
|
if( boundltype==-1 )
|
|
{
|
|
ylen = n-1;
|
|
}
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DGridDiff2Cubic: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, ylen, _state), "Spline1DGridDiff2Cubic: Y contains infinite or NAN values!", _state);
|
|
spline1d_heapsortppoints(x, y, &p, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DGridDiff2Cubic: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Now we've checked and preordered everything,
|
|
* so we can call internal function.
|
|
*
|
|
* After this call we will calculate second derivatives
|
|
* (manually, by converting to the power basis)
|
|
*/
|
|
spline1d_spline1dgriddiffcubicinternal(x, y, n, boundltype, boundl, boundrtype, boundr, d1, &a1, &a2, &a3, &b, &dt, _state);
|
|
ae_vector_set_length(d2, n, _state);
|
|
delta = 0;
|
|
s2 = 0;
|
|
s3 = 0;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
|
|
/*
|
|
* We convert from Hermite basis to the power basis.
|
|
* Si is coefficient before x^i.
|
|
*
|
|
* Inside this cycle we need just S2,
|
|
* because we calculate S'' exactly at spline node,
|
|
* (only x^2 matters at x=0), but after iterations
|
|
* will be over, we will need other coefficients
|
|
* to calculate spline value at the last node.
|
|
*/
|
|
delta = x->ptr.p_double[i+1]-x->ptr.p_double[i];
|
|
delta2 = ae_sqr(delta, _state);
|
|
delta3 = delta*delta2;
|
|
s2 = (3*(y->ptr.p_double[i+1]-y->ptr.p_double[i])-2*d1->ptr.p_double[i]*delta-d1->ptr.p_double[i+1]*delta)/delta2;
|
|
s3 = (2*(y->ptr.p_double[i]-y->ptr.p_double[i+1])+d1->ptr.p_double[i]*delta+d1->ptr.p_double[i+1]*delta)/delta3;
|
|
d2->ptr.p_double[i] = 2*s2;
|
|
}
|
|
d2->ptr.p_double[n-1] = 2*s2+6*s3*delta;
|
|
|
|
/*
|
|
* Remember that HeapSortPPoints() call?
|
|
* Now we have to reorder them back.
|
|
*/
|
|
if( dt.cnt<n )
|
|
{
|
|
ae_vector_set_length(&dt, n, _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
dt.ptr.p_double[p.ptr.p_int[i]] = d1->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&d1->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
dt.ptr.p_double[p.ptr.p_int[i]] = d2->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&d2->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[] (calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvcubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* y2,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _x2;
|
|
ae_vector a1;
|
|
ae_vector a2;
|
|
ae_vector a3;
|
|
ae_vector b;
|
|
ae_vector d;
|
|
ae_vector dt;
|
|
ae_vector d1;
|
|
ae_vector d2;
|
|
ae_vector p;
|
|
ae_vector p2;
|
|
ae_int_t i;
|
|
ae_int_t ylen;
|
|
double t;
|
|
double t2;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_x2, x2, _state, ae_true);
|
|
x2 = &_x2;
|
|
ae_vector_clear(y2);
|
|
ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dt, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&d1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&d2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* check correctness of boundary conditions
|
|
*/
|
|
ae_assert(((boundltype==-1||boundltype==0)||boundltype==1)||boundltype==2, "Spline1DConvCubic: incorrect BoundLType!", _state);
|
|
ae_assert(((boundrtype==-1||boundrtype==0)||boundrtype==1)||boundrtype==2, "Spline1DConvCubic: incorrect BoundRType!", _state);
|
|
ae_assert((boundrtype==-1&&boundltype==-1)||(boundrtype!=-1&&boundltype!=-1), "Spline1DConvCubic: incorrect BoundLType/BoundRType!", _state);
|
|
if( boundltype==1||boundltype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundl, _state), "Spline1DConvCubic: BoundL is infinite or NAN!", _state);
|
|
}
|
|
if( boundrtype==1||boundrtype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundr, _state), "Spline1DConvCubic: BoundR is infinite or NAN!", _state);
|
|
}
|
|
|
|
/*
|
|
* check lengths of arguments
|
|
*/
|
|
ae_assert(n>=2, "Spline1DConvCubic: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DConvCubic: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DConvCubic: Length(Y)<N!", _state);
|
|
ae_assert(n2>=2, "Spline1DConvCubic: N2<2!", _state);
|
|
ae_assert(x2->cnt>=n2, "Spline1DConvCubic: Length(X2)<N2!", _state);
|
|
|
|
/*
|
|
* check and sort X/Y
|
|
*/
|
|
ylen = n;
|
|
if( boundltype==-1 )
|
|
{
|
|
ylen = n-1;
|
|
}
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DConvCubic: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, ylen, _state), "Spline1DConvCubic: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(x2, n2, _state), "Spline1DConvCubic: X2 contains infinite or NAN values!", _state);
|
|
spline1d_heapsortppoints(x, y, &p, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DConvCubic: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* set up DT (we will need it below)
|
|
*/
|
|
ae_vector_set_length(&dt, ae_maxint(n, n2, _state), _state);
|
|
|
|
/*
|
|
* sort X2:
|
|
* * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
|
|
* * if we have periodic problem, wrap points
|
|
* * sort them, store permutation at P2
|
|
*/
|
|
if( boundrtype==-1&&boundltype==-1 )
|
|
{
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
t = x2->ptr.p_double[i];
|
|
apperiodicmap(&t, x->ptr.p_double[0], x->ptr.p_double[n-1], &t2, _state);
|
|
x2->ptr.p_double[i] = t;
|
|
}
|
|
}
|
|
spline1d_heapsortppoints(x2, &dt, &p2, n2, _state);
|
|
|
|
/*
|
|
* Now we've checked and preordered everything, so we:
|
|
* * call internal GridDiff() function to get Hermite form of spline
|
|
* * convert using internal Conv() function
|
|
* * convert Y2 back to original order
|
|
*/
|
|
spline1d_spline1dgriddiffcubicinternal(x, y, n, boundltype, boundl, boundrtype, boundr, &d, &a1, &a2, &a3, &b, &dt, _state);
|
|
spline1dconvdiffinternal(x, y, &d, n, x2, n2, y2, ae_true, &d1, ae_false, &d2, ae_false, _state);
|
|
ae_assert(dt.cnt>=n2, "Spline1DConvCubic: internal error!", _state);
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
dt.ptr.p_double[p2.ptr.p_int[i]] = y2->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&y2->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n2-1));
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[] and derivatives d2[] (calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
D2 - first derivatives at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvdiffcubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* y2,
|
|
/* Real */ ae_vector* d2,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _x2;
|
|
ae_vector a1;
|
|
ae_vector a2;
|
|
ae_vector a3;
|
|
ae_vector b;
|
|
ae_vector d;
|
|
ae_vector dt;
|
|
ae_vector rt1;
|
|
ae_vector p;
|
|
ae_vector p2;
|
|
ae_int_t i;
|
|
ae_int_t ylen;
|
|
double t;
|
|
double t2;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_x2, x2, _state, ae_true);
|
|
x2 = &_x2;
|
|
ae_vector_clear(y2);
|
|
ae_vector_clear(d2);
|
|
ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dt, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&rt1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* check correctness of boundary conditions
|
|
*/
|
|
ae_assert(((boundltype==-1||boundltype==0)||boundltype==1)||boundltype==2, "Spline1DConvDiffCubic: incorrect BoundLType!", _state);
|
|
ae_assert(((boundrtype==-1||boundrtype==0)||boundrtype==1)||boundrtype==2, "Spline1DConvDiffCubic: incorrect BoundRType!", _state);
|
|
ae_assert((boundrtype==-1&&boundltype==-1)||(boundrtype!=-1&&boundltype!=-1), "Spline1DConvDiffCubic: incorrect BoundLType/BoundRType!", _state);
|
|
if( boundltype==1||boundltype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundl, _state), "Spline1DConvDiffCubic: BoundL is infinite or NAN!", _state);
|
|
}
|
|
if( boundrtype==1||boundrtype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundr, _state), "Spline1DConvDiffCubic: BoundR is infinite or NAN!", _state);
|
|
}
|
|
|
|
/*
|
|
* check lengths of arguments
|
|
*/
|
|
ae_assert(n>=2, "Spline1DConvDiffCubic: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DConvDiffCubic: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DConvDiffCubic: Length(Y)<N!", _state);
|
|
ae_assert(n2>=2, "Spline1DConvDiffCubic: N2<2!", _state);
|
|
ae_assert(x2->cnt>=n2, "Spline1DConvDiffCubic: Length(X2)<N2!", _state);
|
|
|
|
/*
|
|
* check and sort X/Y
|
|
*/
|
|
ylen = n;
|
|
if( boundltype==-1 )
|
|
{
|
|
ylen = n-1;
|
|
}
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DConvDiffCubic: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, ylen, _state), "Spline1DConvDiffCubic: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(x2, n2, _state), "Spline1DConvDiffCubic: X2 contains infinite or NAN values!", _state);
|
|
spline1d_heapsortppoints(x, y, &p, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DConvDiffCubic: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* set up DT (we will need it below)
|
|
*/
|
|
ae_vector_set_length(&dt, ae_maxint(n, n2, _state), _state);
|
|
|
|
/*
|
|
* sort X2:
|
|
* * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
|
|
* * if we have periodic problem, wrap points
|
|
* * sort them, store permutation at P2
|
|
*/
|
|
if( boundrtype==-1&&boundltype==-1 )
|
|
{
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
t = x2->ptr.p_double[i];
|
|
apperiodicmap(&t, x->ptr.p_double[0], x->ptr.p_double[n-1], &t2, _state);
|
|
x2->ptr.p_double[i] = t;
|
|
}
|
|
}
|
|
spline1d_heapsortppoints(x2, &dt, &p2, n2, _state);
|
|
|
|
/*
|
|
* Now we've checked and preordered everything, so we:
|
|
* * call internal GridDiff() function to get Hermite form of spline
|
|
* * convert using internal Conv() function
|
|
* * convert Y2 back to original order
|
|
*/
|
|
spline1d_spline1dgriddiffcubicinternal(x, y, n, boundltype, boundl, boundrtype, boundr, &d, &a1, &a2, &a3, &b, &dt, _state);
|
|
spline1dconvdiffinternal(x, y, &d, n, x2, n2, y2, ae_true, d2, ae_true, &rt1, ae_false, _state);
|
|
ae_assert(dt.cnt>=n2, "Spline1DConvDiffCubic: internal error!", _state);
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
dt.ptr.p_double[p2.ptr.p_int[i]] = y2->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&y2->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n2-1));
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
dt.ptr.p_double[p2.ptr.p_int[i]] = d2->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&d2->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n2-1));
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function solves following problem: given table y[] of function values
|
|
at old nodes x[] and new nodes x2[], it calculates and returns table of
|
|
function values y2[], first and second derivatives d2[] and dd2[]
|
|
(calculated at x2[]).
|
|
|
|
This function yields same result as Spline1DBuildCubic() call followed by
|
|
sequence of Spline1DDiff() calls, but it can be several times faster when
|
|
called for ordered X[] and X2[].
|
|
|
|
INPUT PARAMETERS:
|
|
X - old spline nodes
|
|
Y - function values
|
|
X2 - new spline nodes
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points from X/Y are used
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundLType - boundary condition type for the left boundary
|
|
BoundL - left boundary condition (first or second derivative,
|
|
depending on the BoundLType)
|
|
BoundRType - boundary condition type for the right boundary
|
|
BoundR - right boundary condition (first or second derivative,
|
|
depending on the BoundRType)
|
|
N2 - new points count:
|
|
* N2>=2
|
|
* if given, only first N2 points from X2 are used
|
|
* if not given, automatically detected from X2 size
|
|
|
|
OUTPUT PARAMETERS:
|
|
F2 - function values at X2[]
|
|
D2 - first derivatives at X2[]
|
|
DD2 - second derivatives at X2[]
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
Function values are correctly reordered on return, so F2[I] is always
|
|
equal to S(X2[I]) independently of points order.
|
|
|
|
SETTING BOUNDARY VALUES:
|
|
|
|
The BoundLType/BoundRType parameters can have the following values:
|
|
* -1, which corresonds to the periodic (cyclic) boundary conditions.
|
|
In this case:
|
|
* both BoundLType and BoundRType must be equal to -1.
|
|
* BoundL/BoundR are ignored
|
|
* Y[last] is ignored (it is assumed to be equal to Y[first]).
|
|
* 0, which corresponds to the parabolically terminated spline
|
|
(BoundL and/or BoundR are ignored).
|
|
* 1, which corresponds to the first derivative boundary condition
|
|
* 2, which corresponds to the second derivative boundary condition
|
|
* by default, BoundType=0 is used
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvdiff2cubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* y2,
|
|
/* Real */ ae_vector* d2,
|
|
/* Real */ ae_vector* dd2,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _x2;
|
|
ae_vector a1;
|
|
ae_vector a2;
|
|
ae_vector a3;
|
|
ae_vector b;
|
|
ae_vector d;
|
|
ae_vector dt;
|
|
ae_vector p;
|
|
ae_vector p2;
|
|
ae_int_t i;
|
|
ae_int_t ylen;
|
|
double t;
|
|
double t2;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_x2, x2, _state, ae_true);
|
|
x2 = &_x2;
|
|
ae_vector_clear(y2);
|
|
ae_vector_clear(d2);
|
|
ae_vector_clear(dd2);
|
|
ae_vector_init(&a1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&a3, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dt, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* check correctness of boundary conditions
|
|
*/
|
|
ae_assert(((boundltype==-1||boundltype==0)||boundltype==1)||boundltype==2, "Spline1DConvDiff2Cubic: incorrect BoundLType!", _state);
|
|
ae_assert(((boundrtype==-1||boundrtype==0)||boundrtype==1)||boundrtype==2, "Spline1DConvDiff2Cubic: incorrect BoundRType!", _state);
|
|
ae_assert((boundrtype==-1&&boundltype==-1)||(boundrtype!=-1&&boundltype!=-1), "Spline1DConvDiff2Cubic: incorrect BoundLType/BoundRType!", _state);
|
|
if( boundltype==1||boundltype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundl, _state), "Spline1DConvDiff2Cubic: BoundL is infinite or NAN!", _state);
|
|
}
|
|
if( boundrtype==1||boundrtype==2 )
|
|
{
|
|
ae_assert(ae_isfinite(boundr, _state), "Spline1DConvDiff2Cubic: BoundR is infinite or NAN!", _state);
|
|
}
|
|
|
|
/*
|
|
* check lengths of arguments
|
|
*/
|
|
ae_assert(n>=2, "Spline1DConvDiff2Cubic: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DConvDiff2Cubic: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DConvDiff2Cubic: Length(Y)<N!", _state);
|
|
ae_assert(n2>=2, "Spline1DConvDiff2Cubic: N2<2!", _state);
|
|
ae_assert(x2->cnt>=n2, "Spline1DConvDiff2Cubic: Length(X2)<N2!", _state);
|
|
|
|
/*
|
|
* check and sort X/Y
|
|
*/
|
|
ylen = n;
|
|
if( boundltype==-1 )
|
|
{
|
|
ylen = n-1;
|
|
}
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DConvDiff2Cubic: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, ylen, _state), "Spline1DConvDiff2Cubic: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(x2, n2, _state), "Spline1DConvDiff2Cubic: X2 contains infinite or NAN values!", _state);
|
|
spline1d_heapsortppoints(x, y, &p, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DConvDiff2Cubic: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* set up DT (we will need it below)
|
|
*/
|
|
ae_vector_set_length(&dt, ae_maxint(n, n2, _state), _state);
|
|
|
|
/*
|
|
* sort X2:
|
|
* * use fake array DT because HeapSortPPoints() needs both integer AND real arrays
|
|
* * if we have periodic problem, wrap points
|
|
* * sort them, store permutation at P2
|
|
*/
|
|
if( boundrtype==-1&&boundltype==-1 )
|
|
{
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
t = x2->ptr.p_double[i];
|
|
apperiodicmap(&t, x->ptr.p_double[0], x->ptr.p_double[n-1], &t2, _state);
|
|
x2->ptr.p_double[i] = t;
|
|
}
|
|
}
|
|
spline1d_heapsortppoints(x2, &dt, &p2, n2, _state);
|
|
|
|
/*
|
|
* Now we've checked and preordered everything, so we:
|
|
* * call internal GridDiff() function to get Hermite form of spline
|
|
* * convert using internal Conv() function
|
|
* * convert Y2 back to original order
|
|
*/
|
|
spline1d_spline1dgriddiffcubicinternal(x, y, n, boundltype, boundl, boundrtype, boundr, &d, &a1, &a2, &a3, &b, &dt, _state);
|
|
spline1dconvdiffinternal(x, y, &d, n, x2, n2, y2, ae_true, d2, ae_true, dd2, ae_true, _state);
|
|
ae_assert(dt.cnt>=n2, "Spline1DConvDiff2Cubic: internal error!", _state);
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
dt.ptr.p_double[p2.ptr.p_int[i]] = y2->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&y2->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n2-1));
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
dt.ptr.p_double[p2.ptr.p_int[i]] = d2->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&d2->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n2-1));
|
|
for(i=0; i<=n2-1; i++)
|
|
{
|
|
dt.ptr.p_double[p2.ptr.p_int[i]] = dd2->ptr.p_double[i];
|
|
}
|
|
ae_v_move(&dd2->ptr.p_double[0], 1, &dt.ptr.p_double[0], 1, ae_v_len(0,n2-1));
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Catmull-Rom spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
|
|
OPTIONAL PARAMETERS:
|
|
N - points count:
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
BoundType - boundary condition type:
|
|
* -1 for periodic boundary condition
|
|
* 0 for parabolically terminated spline (default)
|
|
Tension - tension parameter:
|
|
* tension=0 corresponds to classic Catmull-Rom spline (default)
|
|
* 0<tension<1 corresponds to more general form - cardinal spline
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
PROBLEMS WITH PERIODIC BOUNDARY CONDITIONS:
|
|
|
|
Problems with periodic boundary conditions have Y[first_point]=Y[last_point].
|
|
However, this subroutine doesn't require you to specify equal values for
|
|
the first and last points - it automatically forces them to be equal by
|
|
copying Y[first_point] (corresponds to the leftmost, minimal X[]) to
|
|
Y[last_point]. However it is recommended to pass consistent values of Y[],
|
|
i.e. to make Y[first_point]=Y[last_point].
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildcatmullrom(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundtype,
|
|
double tension,
|
|
spline1dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector d;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
_spline1dinterpolant_clear(c);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=2, "Spline1DBuildCatmullRom: N<2!", _state);
|
|
ae_assert(boundtype==-1||boundtype==0, "Spline1DBuildCatmullRom: incorrect BoundType!", _state);
|
|
ae_assert(ae_fp_greater_eq(tension,0), "Spline1DBuildCatmullRom: Tension<0!", _state);
|
|
ae_assert(ae_fp_less_eq(tension,1), "Spline1DBuildCatmullRom: Tension>1!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DBuildCatmullRom: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DBuildCatmullRom: Length(Y)<N!", _state);
|
|
|
|
/*
|
|
* check and sort points
|
|
*/
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DBuildCatmullRom: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DBuildCatmullRom: Y contains infinite or NAN values!", _state);
|
|
spline1d_heapsortpoints(x, y, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DBuildCatmullRom: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Special cases:
|
|
* * N=2, parabolic terminated boundary condition on both ends
|
|
* * N=2, periodic boundary condition
|
|
*/
|
|
if( n==2&&boundtype==0 )
|
|
{
|
|
|
|
/*
|
|
* Just linear spline
|
|
*/
|
|
spline1dbuildlinear(x, y, n, c, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( n==2&&boundtype==-1 )
|
|
{
|
|
|
|
/*
|
|
* Same as cubic spline with periodic conditions
|
|
*/
|
|
spline1dbuildcubic(x, y, n, -1, 0.0, -1, 0.0, c, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Periodic or non-periodic boundary conditions
|
|
*/
|
|
if( boundtype==-1 )
|
|
{
|
|
|
|
/*
|
|
* Periodic boundary conditions
|
|
*/
|
|
y->ptr.p_double[n-1] = y->ptr.p_double[0];
|
|
ae_vector_set_length(&d, n, _state);
|
|
d.ptr.p_double[0] = (y->ptr.p_double[1]-y->ptr.p_double[n-2])/(2*(x->ptr.p_double[1]-x->ptr.p_double[0]+x->ptr.p_double[n-1]-x->ptr.p_double[n-2]));
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
d.ptr.p_double[i] = (1-tension)*(y->ptr.p_double[i+1]-y->ptr.p_double[i-1])/(x->ptr.p_double[i+1]-x->ptr.p_double[i-1]);
|
|
}
|
|
d.ptr.p_double[n-1] = d.ptr.p_double[0];
|
|
|
|
/*
|
|
* Now problem is reduced to the cubic Hermite spline
|
|
*/
|
|
spline1dbuildhermite(x, y, &d, n, c, _state);
|
|
c->periodic = ae_true;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Non-periodic boundary conditions
|
|
*/
|
|
ae_vector_set_length(&d, n, _state);
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
d.ptr.p_double[i] = (1-tension)*(y->ptr.p_double[i+1]-y->ptr.p_double[i-1])/(x->ptr.p_double[i+1]-x->ptr.p_double[i-1]);
|
|
}
|
|
d.ptr.p_double[0] = 2*(y->ptr.p_double[1]-y->ptr.p_double[0])/(x->ptr.p_double[1]-x->ptr.p_double[0])-d.ptr.p_double[1];
|
|
d.ptr.p_double[n-1] = 2*(y->ptr.p_double[n-1]-y->ptr.p_double[n-2])/(x->ptr.p_double[n-1]-x->ptr.p_double[n-2])-d.ptr.p_double[n-2];
|
|
|
|
/*
|
|
* Now problem is reduced to the cubic Hermite spline
|
|
*/
|
|
spline1dbuildhermite(x, y, &d, n, c, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Hermite spline interpolant.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
D - derivatives, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildhermite(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* d,
|
|
ae_int_t n,
|
|
spline1dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _d;
|
|
ae_int_t i;
|
|
double delta;
|
|
double delta2;
|
|
double delta3;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_d, d, _state, ae_true);
|
|
d = &_d;
|
|
_spline1dinterpolant_clear(c);
|
|
|
|
ae_assert(n>=2, "Spline1DBuildHermite: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DBuildHermite: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DBuildHermite: Length(Y)<N!", _state);
|
|
ae_assert(d->cnt>=n, "Spline1DBuildHermite: Length(D)<N!", _state);
|
|
|
|
/*
|
|
* check and sort points
|
|
*/
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DBuildHermite: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DBuildHermite: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(d, n, _state), "Spline1DBuildHermite: D contains infinite or NAN values!", _state);
|
|
heapsortdpoints(x, y, d, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DBuildHermite: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Build
|
|
*/
|
|
ae_vector_set_length(&c->x, n, _state);
|
|
ae_vector_set_length(&c->c, 4*(n-1)+2, _state);
|
|
c->periodic = ae_false;
|
|
c->k = 3;
|
|
c->n = n;
|
|
c->continuity = 1;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
c->x.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
delta = x->ptr.p_double[i+1]-x->ptr.p_double[i];
|
|
delta2 = ae_sqr(delta, _state);
|
|
delta3 = delta*delta2;
|
|
c->c.ptr.p_double[4*i+0] = y->ptr.p_double[i];
|
|
c->c.ptr.p_double[4*i+1] = d->ptr.p_double[i];
|
|
c->c.ptr.p_double[4*i+2] = (3*(y->ptr.p_double[i+1]-y->ptr.p_double[i])-2*d->ptr.p_double[i]*delta-d->ptr.p_double[i+1]*delta)/delta2;
|
|
c->c.ptr.p_double[4*i+3] = (2*(y->ptr.p_double[i]-y->ptr.p_double[i+1])+d->ptr.p_double[i]*delta+d->ptr.p_double[i+1]*delta)/delta3;
|
|
}
|
|
c->c.ptr.p_double[4*(n-1)+0] = y->ptr.p_double[n-1];
|
|
c->c.ptr.p_double[4*(n-1)+1] = d->ptr.p_double[n-1];
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds Akima spline interpolant
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]
|
|
Y - function values, array[0..N-1]
|
|
N - points count (optional):
|
|
* N>=2
|
|
* if given, only first N points are used to build spline
|
|
* if not given, automatically detected from X/Y sizes
|
|
(len(X) must be equal to len(Y))
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildakima(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
spline1dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_int_t i;
|
|
ae_vector d;
|
|
ae_vector w;
|
|
ae_vector diff;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
_spline1dinterpolant_clear(c);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&diff, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=2, "Spline1DBuildAkima: N<2!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DBuildAkima: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DBuildAkima: Length(Y)<N!", _state);
|
|
|
|
/*
|
|
* check and sort points
|
|
*/
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DBuildAkima: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DBuildAkima: Y contains infinite or NAN values!", _state);
|
|
spline1d_heapsortpoints(x, y, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DBuildAkima: at least two consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Handle special cases: N=2, N=3, N=4
|
|
*/
|
|
if( n<=4 )
|
|
{
|
|
spline1dbuildcubic(x, y, n, 0, 0.0, 0, 0.0, c, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Prepare W (weights), Diff (divided differences)
|
|
*/
|
|
ae_vector_set_length(&w, n-1, _state);
|
|
ae_vector_set_length(&diff, n-1, _state);
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
diff.ptr.p_double[i] = (y->ptr.p_double[i+1]-y->ptr.p_double[i])/(x->ptr.p_double[i+1]-x->ptr.p_double[i]);
|
|
}
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
w.ptr.p_double[i] = ae_fabs(diff.ptr.p_double[i]-diff.ptr.p_double[i-1], _state);
|
|
}
|
|
|
|
/*
|
|
* Prepare Hermite interpolation scheme
|
|
*/
|
|
ae_vector_set_length(&d, n, _state);
|
|
for(i=2; i<=n-3; i++)
|
|
{
|
|
if( ae_fp_neq(ae_fabs(w.ptr.p_double[i-1], _state)+ae_fabs(w.ptr.p_double[i+1], _state),0) )
|
|
{
|
|
d.ptr.p_double[i] = (w.ptr.p_double[i+1]*diff.ptr.p_double[i-1]+w.ptr.p_double[i-1]*diff.ptr.p_double[i])/(w.ptr.p_double[i+1]+w.ptr.p_double[i-1]);
|
|
}
|
|
else
|
|
{
|
|
d.ptr.p_double[i] = ((x->ptr.p_double[i+1]-x->ptr.p_double[i])*diff.ptr.p_double[i-1]+(x->ptr.p_double[i]-x->ptr.p_double[i-1])*diff.ptr.p_double[i])/(x->ptr.p_double[i+1]-x->ptr.p_double[i-1]);
|
|
}
|
|
}
|
|
d.ptr.p_double[0] = spline1d_diffthreepoint(x->ptr.p_double[0], x->ptr.p_double[0], y->ptr.p_double[0], x->ptr.p_double[1], y->ptr.p_double[1], x->ptr.p_double[2], y->ptr.p_double[2], _state);
|
|
d.ptr.p_double[1] = spline1d_diffthreepoint(x->ptr.p_double[1], x->ptr.p_double[0], y->ptr.p_double[0], x->ptr.p_double[1], y->ptr.p_double[1], x->ptr.p_double[2], y->ptr.p_double[2], _state);
|
|
d.ptr.p_double[n-2] = spline1d_diffthreepoint(x->ptr.p_double[n-2], x->ptr.p_double[n-3], y->ptr.p_double[n-3], x->ptr.p_double[n-2], y->ptr.p_double[n-2], x->ptr.p_double[n-1], y->ptr.p_double[n-1], _state);
|
|
d.ptr.p_double[n-1] = spline1d_diffthreepoint(x->ptr.p_double[n-1], x->ptr.p_double[n-3], y->ptr.p_double[n-3], x->ptr.p_double[n-2], y->ptr.p_double[n-2], x->ptr.p_double[n-1], y->ptr.p_double[n-1], _state);
|
|
|
|
/*
|
|
* Build Akima spline using Hermite interpolation scheme
|
|
*/
|
|
spline1dbuildhermite(x, y, &d, n, c, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the spline at the given point X.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant
|
|
X - point
|
|
|
|
Result:
|
|
S(x)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline1dcalc(spline1dinterpolant* c, double x, ae_state *_state)
|
|
{
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t m;
|
|
double t;
|
|
double result;
|
|
|
|
|
|
ae_assert(c->k==3, "Spline1DCalc: internal error", _state);
|
|
ae_assert(!ae_isinf(x, _state), "Spline1DCalc: infinite X!", _state);
|
|
|
|
/*
|
|
* special case: NaN
|
|
*/
|
|
if( ae_isnan(x, _state) )
|
|
{
|
|
result = _state->v_nan;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* correct if periodic
|
|
*/
|
|
if( c->periodic )
|
|
{
|
|
apperiodicmap(&x, c->x.ptr.p_double[0], c->x.ptr.p_double[c->n-1], &t, _state);
|
|
}
|
|
|
|
/*
|
|
* Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->n-2+1;
|
|
while(l!=r-1)
|
|
{
|
|
m = (l+r)/2;
|
|
if( c->x.ptr.p_double[m]>=x )
|
|
{
|
|
r = m;
|
|
}
|
|
else
|
|
{
|
|
l = m;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Interpolation
|
|
*/
|
|
x = x-c->x.ptr.p_double[l];
|
|
m = 4*l;
|
|
result = c->c.ptr.p_double[m]+x*(c->c.ptr.p_double[m+1]+x*(c->c.ptr.p_double[m+2]+x*c->c.ptr.p_double[m+3]));
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine differentiates the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X - point
|
|
|
|
Result:
|
|
S - S(x)
|
|
DS - S'(x)
|
|
D2S - S''(x)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 24.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1ddiff(spline1dinterpolant* c,
|
|
double x,
|
|
double* s,
|
|
double* ds,
|
|
double* d2s,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t m;
|
|
double t;
|
|
|
|
*s = 0;
|
|
*ds = 0;
|
|
*d2s = 0;
|
|
|
|
ae_assert(c->k==3, "Spline1DDiff: internal error", _state);
|
|
ae_assert(!ae_isinf(x, _state), "Spline1DDiff: infinite X!", _state);
|
|
|
|
/*
|
|
* special case: NaN
|
|
*/
|
|
if( ae_isnan(x, _state) )
|
|
{
|
|
*s = _state->v_nan;
|
|
*ds = _state->v_nan;
|
|
*d2s = _state->v_nan;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* correct if periodic
|
|
*/
|
|
if( c->periodic )
|
|
{
|
|
apperiodicmap(&x, c->x.ptr.p_double[0], c->x.ptr.p_double[c->n-1], &t, _state);
|
|
}
|
|
|
|
/*
|
|
* Binary search
|
|
*/
|
|
l = 0;
|
|
r = c->n-2+1;
|
|
while(l!=r-1)
|
|
{
|
|
m = (l+r)/2;
|
|
if( c->x.ptr.p_double[m]>=x )
|
|
{
|
|
r = m;
|
|
}
|
|
else
|
|
{
|
|
l = m;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Differentiation
|
|
*/
|
|
x = x-c->x.ptr.p_double[l];
|
|
m = 4*l;
|
|
*s = c->c.ptr.p_double[m]+x*(c->c.ptr.p_double[m+1]+x*(c->c.ptr.p_double[m+2]+x*c->c.ptr.p_double[m+3]));
|
|
*ds = c->c.ptr.p_double[m+1]+2*x*c->c.ptr.p_double[m+2]+3*ae_sqr(x, _state)*c->c.ptr.p_double[m+3];
|
|
*d2s = 2*c->c.ptr.p_double[m+2]+6*x*c->c.ptr.p_double[m+3];
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine makes the copy of the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
Result:
|
|
CC - spline copy
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dcopy(spline1dinterpolant* c,
|
|
spline1dinterpolant* cc,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t s;
|
|
|
|
_spline1dinterpolant_clear(cc);
|
|
|
|
cc->periodic = c->periodic;
|
|
cc->n = c->n;
|
|
cc->k = c->k;
|
|
cc->continuity = c->continuity;
|
|
ae_vector_set_length(&cc->x, cc->n, _state);
|
|
ae_v_move(&cc->x.ptr.p_double[0], 1, &c->x.ptr.p_double[0], 1, ae_v_len(0,cc->n-1));
|
|
s = c->c.cnt;
|
|
ae_vector_set_length(&cc->c, s, _state);
|
|
ae_v_move(&cc->c.ptr.p_double[0], 1, &c->c.ptr.p_double[0], 1, ae_v_len(0,s-1));
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine unpacks the spline into the coefficients table.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X - point
|
|
|
|
OUTPUT PARAMETERS:
|
|
Tbl - coefficients table, unpacked format, array[0..N-2, 0..5].
|
|
For I = 0...N-2:
|
|
Tbl[I,0] = X[i]
|
|
Tbl[I,1] = X[i+1]
|
|
Tbl[I,2] = C0
|
|
Tbl[I,3] = C1
|
|
Tbl[I,4] = C2
|
|
Tbl[I,5] = C3
|
|
On [x[i], x[i+1]] spline is equals to:
|
|
S(x) = C0 + C1*t + C2*t^2 + C3*t^3
|
|
t = x-x[i]
|
|
|
|
NOTE:
|
|
You can rebuild spline with Spline1DBuildHermite() function, which
|
|
accepts as inputs function values and derivatives at nodes, which are
|
|
easy to calculate when you have coefficients.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dunpack(spline1dinterpolant* c,
|
|
ae_int_t* n,
|
|
/* Real */ ae_matrix* tbl,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
*n = 0;
|
|
ae_matrix_clear(tbl);
|
|
|
|
ae_matrix_set_length(tbl, c->n-2+1, 2+c->k+1, _state);
|
|
*n = c->n;
|
|
|
|
/*
|
|
* Fill
|
|
*/
|
|
for(i=0; i<=*n-2; i++)
|
|
{
|
|
tbl->ptr.pp_double[i][0] = c->x.ptr.p_double[i];
|
|
tbl->ptr.pp_double[i][1] = c->x.ptr.p_double[i+1];
|
|
for(j=0; j<=c->k; j++)
|
|
{
|
|
tbl->ptr.pp_double[i][2+j] = c->c.ptr.p_double[(c->k+1)*i+j];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline argument.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: x = A*t + B
|
|
Result:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dlintransx(spline1dinterpolant* c,
|
|
double a,
|
|
double b,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t n;
|
|
double v;
|
|
double dv;
|
|
double d2v;
|
|
ae_vector x;
|
|
ae_vector y;
|
|
ae_vector d;
|
|
ae_bool isperiodic;
|
|
ae_int_t contval;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(c->k==3, "Spline1DLinTransX: internal error", _state);
|
|
n = c->n;
|
|
ae_vector_set_length(&x, n, _state);
|
|
ae_vector_set_length(&y, n, _state);
|
|
ae_vector_set_length(&d, n, _state);
|
|
|
|
/*
|
|
* Unpack, X, Y, dY/dX.
|
|
* Scale and pack with Spline1DBuildHermite again.
|
|
*/
|
|
if( ae_fp_eq(a,0) )
|
|
{
|
|
|
|
/*
|
|
* Special case: A=0
|
|
*/
|
|
v = spline1dcalc(c, b, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
x.ptr.p_double[i] = c->x.ptr.p_double[i];
|
|
y.ptr.p_double[i] = v;
|
|
d.ptr.p_double[i] = 0.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* General case, A<>0
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
x.ptr.p_double[i] = c->x.ptr.p_double[i];
|
|
spline1ddiff(c, x.ptr.p_double[i], &v, &dv, &d2v, _state);
|
|
x.ptr.p_double[i] = (x.ptr.p_double[i]-b)/a;
|
|
y.ptr.p_double[i] = v;
|
|
d.ptr.p_double[i] = a*dv;
|
|
}
|
|
}
|
|
isperiodic = c->periodic;
|
|
contval = c->continuity;
|
|
if( contval>0 )
|
|
{
|
|
spline1dbuildhermite(&x, &y, &d, n, c, _state);
|
|
}
|
|
else
|
|
{
|
|
spline1dbuildlinear(&x, &y, n, c, _state);
|
|
}
|
|
c->periodic = isperiodic;
|
|
c->continuity = contval;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: S2(x) = A*S(x) + B
|
|
Result:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dlintransy(spline1dinterpolant* c,
|
|
double a,
|
|
double b,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t n;
|
|
|
|
|
|
ae_assert(c->k==3, "Spline1DLinTransX: internal error", _state);
|
|
n = c->n;
|
|
for(i=0; i<=n-2; i++)
|
|
{
|
|
c->c.ptr.p_double[4*i] = a*c->c.ptr.p_double[4*i]+b;
|
|
for(j=1; j<=3; j++)
|
|
{
|
|
c->c.ptr.p_double[4*i+j] = a*c->c.ptr.p_double[4*i+j];
|
|
}
|
|
}
|
|
c->c.ptr.p_double[4*(n-1)+0] = a*c->c.ptr.p_double[4*(n-1)+0]+b;
|
|
c->c.ptr.p_double[4*(n-1)+1] = a*c->c.ptr.p_double[4*(n-1)+1];
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine integrates the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X - right bound of the integration interval [a, x],
|
|
here 'a' denotes min(x[])
|
|
Result:
|
|
integral(S(t)dt,a,x)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 23.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline1dintegrate(spline1dinterpolant* c,
|
|
double x,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t n;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t m;
|
|
double w;
|
|
double v;
|
|
double t;
|
|
double intab;
|
|
double additionalterm;
|
|
double result;
|
|
|
|
|
|
n = c->n;
|
|
|
|
/*
|
|
* Periodic splines require special treatment. We make
|
|
* following transformation:
|
|
*
|
|
* integral(S(t)dt,A,X) = integral(S(t)dt,A,Z)+AdditionalTerm
|
|
*
|
|
* here X may lie outside of [A,B], Z lies strictly in [A,B],
|
|
* AdditionalTerm is equals to integral(S(t)dt,A,B) times some
|
|
* integer number (may be zero).
|
|
*/
|
|
if( c->periodic&&(ae_fp_less(x,c->x.ptr.p_double[0])||ae_fp_greater(x,c->x.ptr.p_double[c->n-1])) )
|
|
{
|
|
|
|
/*
|
|
* compute integral(S(x)dx,A,B)
|
|
*/
|
|
intab = 0;
|
|
for(i=0; i<=c->n-2; i++)
|
|
{
|
|
w = c->x.ptr.p_double[i+1]-c->x.ptr.p_double[i];
|
|
m = (c->k+1)*i;
|
|
intab = intab+c->c.ptr.p_double[m]*w;
|
|
v = w;
|
|
for(j=1; j<=c->k; j++)
|
|
{
|
|
v = v*w;
|
|
intab = intab+c->c.ptr.p_double[m+j]*v/(j+1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* map X into [A,B]
|
|
*/
|
|
apperiodicmap(&x, c->x.ptr.p_double[0], c->x.ptr.p_double[c->n-1], &t, _state);
|
|
additionalterm = t*intab;
|
|
}
|
|
else
|
|
{
|
|
additionalterm = 0;
|
|
}
|
|
|
|
/*
|
|
* Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = n-2+1;
|
|
while(l!=r-1)
|
|
{
|
|
m = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->x.ptr.p_double[m],x) )
|
|
{
|
|
r = m;
|
|
}
|
|
else
|
|
{
|
|
l = m;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Integration
|
|
*/
|
|
result = 0;
|
|
for(i=0; i<=l-1; i++)
|
|
{
|
|
w = c->x.ptr.p_double[i+1]-c->x.ptr.p_double[i];
|
|
m = (c->k+1)*i;
|
|
result = result+c->c.ptr.p_double[m]*w;
|
|
v = w;
|
|
for(j=1; j<=c->k; j++)
|
|
{
|
|
v = v*w;
|
|
result = result+c->c.ptr.p_double[m+j]*v/(j+1);
|
|
}
|
|
}
|
|
w = x-c->x.ptr.p_double[l];
|
|
m = (c->k+1)*l;
|
|
v = w;
|
|
result = result+c->c.ptr.p_double[m]*w;
|
|
for(j=1; j<=c->k; j++)
|
|
{
|
|
v = v*w;
|
|
result = result+c->c.ptr.p_double[m+j]*v/(j+1);
|
|
}
|
|
result = result+additionalterm;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal version of Spline1DConvDiff
|
|
|
|
Converts from Hermite spline given by grid XOld to new grid X2
|
|
|
|
INPUT PARAMETERS:
|
|
XOld - old grid
|
|
YOld - values at old grid
|
|
DOld - first derivative at old grid
|
|
N - grid size
|
|
X2 - new grid
|
|
N2 - new grid size
|
|
Y - possibly preallocated output array
|
|
(reallocate if too small)
|
|
NeedY - do we need Y?
|
|
D1 - possibly preallocated output array
|
|
(reallocate if too small)
|
|
NeedD1 - do we need D1?
|
|
D2 - possibly preallocated output array
|
|
(reallocate if too small)
|
|
NeedD2 - do we need D1?
|
|
|
|
OUTPUT ARRAYS:
|
|
Y - values, if needed
|
|
D1 - first derivative, if needed
|
|
D2 - second derivative, if needed
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dconvdiffinternal(/* Real */ ae_vector* xold,
|
|
/* Real */ ae_vector* yold,
|
|
/* Real */ ae_vector* dold,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* y,
|
|
ae_bool needy,
|
|
/* Real */ ae_vector* d1,
|
|
ae_bool needd1,
|
|
/* Real */ ae_vector* d2,
|
|
ae_bool needd2,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t intervalindex;
|
|
ae_int_t pointindex;
|
|
ae_bool havetoadvance;
|
|
double c0;
|
|
double c1;
|
|
double c2;
|
|
double c3;
|
|
double a;
|
|
double b;
|
|
double w;
|
|
double w2;
|
|
double w3;
|
|
double fa;
|
|
double fb;
|
|
double da;
|
|
double db;
|
|
double t;
|
|
|
|
|
|
|
|
/*
|
|
* Prepare space
|
|
*/
|
|
if( needy&&y->cnt<n2 )
|
|
{
|
|
ae_vector_set_length(y, n2, _state);
|
|
}
|
|
if( needd1&&d1->cnt<n2 )
|
|
{
|
|
ae_vector_set_length(d1, n2, _state);
|
|
}
|
|
if( needd2&&d2->cnt<n2 )
|
|
{
|
|
ae_vector_set_length(d2, n2, _state);
|
|
}
|
|
|
|
/*
|
|
* These assignments aren't actually needed
|
|
* (variables are initialized in the loop below),
|
|
* but without them compiler will complain about uninitialized locals
|
|
*/
|
|
c0 = 0;
|
|
c1 = 0;
|
|
c2 = 0;
|
|
c3 = 0;
|
|
a = 0;
|
|
b = 0;
|
|
|
|
/*
|
|
* Cycle
|
|
*/
|
|
intervalindex = -1;
|
|
pointindex = 0;
|
|
for(;;)
|
|
{
|
|
|
|
/*
|
|
* are we ready to exit?
|
|
*/
|
|
if( pointindex>=n2 )
|
|
{
|
|
break;
|
|
}
|
|
t = x2->ptr.p_double[pointindex];
|
|
|
|
/*
|
|
* do we need to advance interval?
|
|
*/
|
|
havetoadvance = ae_false;
|
|
if( intervalindex==-1 )
|
|
{
|
|
havetoadvance = ae_true;
|
|
}
|
|
else
|
|
{
|
|
if( intervalindex<n-2 )
|
|
{
|
|
havetoadvance = ae_fp_greater_eq(t,b);
|
|
}
|
|
}
|
|
if( havetoadvance )
|
|
{
|
|
intervalindex = intervalindex+1;
|
|
a = xold->ptr.p_double[intervalindex];
|
|
b = xold->ptr.p_double[intervalindex+1];
|
|
w = b-a;
|
|
w2 = w*w;
|
|
w3 = w*w2;
|
|
fa = yold->ptr.p_double[intervalindex];
|
|
fb = yold->ptr.p_double[intervalindex+1];
|
|
da = dold->ptr.p_double[intervalindex];
|
|
db = dold->ptr.p_double[intervalindex+1];
|
|
c0 = fa;
|
|
c1 = da;
|
|
c2 = (3*(fb-fa)-2*da*w-db*w)/w2;
|
|
c3 = (2*(fa-fb)+da*w+db*w)/w3;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Calculate spline and its derivatives using power basis
|
|
*/
|
|
t = t-a;
|
|
if( needy )
|
|
{
|
|
y->ptr.p_double[pointindex] = c0+t*(c1+t*(c2+t*c3));
|
|
}
|
|
if( needd1 )
|
|
{
|
|
d1->ptr.p_double[pointindex] = c1+2*t*c2+3*t*t*c3;
|
|
}
|
|
if( needd2 )
|
|
{
|
|
d2->ptr.p_double[pointindex] = 2*c2+6*t*c3;
|
|
}
|
|
pointindex = pointindex+1;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function finds all roots and extrema of the spline S(x) defined at
|
|
[A,B] (interval which contains spline nodes).
|
|
|
|
It does not extrapolates function, so roots and extrema located outside
|
|
of [A,B] will not be found. It returns all isolated (including multiple)
|
|
roots and extrema.
|
|
|
|
INPUT PARAMETERS
|
|
C - spline interpolant
|
|
|
|
OUTPUT PARAMETERS
|
|
R - array[NR], contains roots of the spline.
|
|
In case there is no roots, this array has zero length.
|
|
NR - number of roots, >=0
|
|
DR - is set to True in case there is at least one interval
|
|
where spline is just a zero constant. Such degenerate
|
|
cases are not reported in the R/NR
|
|
E - array[NE], contains extrema (maximums/minimums) of
|
|
the spline. In case there is no extrema, this array
|
|
has zero length.
|
|
ET - array[NE], extrema types:
|
|
* ET[i]>0 in case I-th extrema is a minimum
|
|
* ET[i]<0 in case I-th extrema is a maximum
|
|
NE - number of extrema, >=0
|
|
DE - is set to True in case there is at least one interval
|
|
where spline is a constant. Such degenerate cases are
|
|
not reported in the E/NE.
|
|
|
|
NOTES:
|
|
|
|
1. This function does NOT report following kinds of roots:
|
|
* intervals where function is constantly zero
|
|
* roots which are outside of [A,B] (note: it CAN return A or B)
|
|
|
|
2. This function does NOT report following kinds of extrema:
|
|
* intervals where function is a constant
|
|
* extrema which are outside of (A,B) (note: it WON'T return A or B)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.09.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1drootsandextrema(spline1dinterpolant* c,
|
|
/* Real */ ae_vector* r,
|
|
ae_int_t* nr,
|
|
ae_bool* dr,
|
|
/* Real */ ae_vector* e,
|
|
/* Integer */ ae_vector* et,
|
|
ae_int_t* ne,
|
|
ae_bool* de,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
double pl;
|
|
double ml;
|
|
double pll;
|
|
double pr;
|
|
double mr;
|
|
ae_vector tr;
|
|
ae_vector tmpr;
|
|
ae_vector tmpe;
|
|
ae_vector tmpet;
|
|
ae_vector tmpc;
|
|
double x0;
|
|
double x1;
|
|
double x2;
|
|
double ex0;
|
|
double ex1;
|
|
ae_int_t tne;
|
|
ae_int_t tnr;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_bool nstep;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_clear(r);
|
|
*nr = 0;
|
|
*dr = ae_false;
|
|
ae_vector_clear(e);
|
|
ae_vector_clear(et);
|
|
*ne = 0;
|
|
*de = ae_false;
|
|
ae_vector_init(&tr, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpr, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpe, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpet, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&tmpc, 0, DT_REAL, _state, ae_true);
|
|
|
|
|
|
/*
|
|
*exception handling
|
|
*/
|
|
ae_assert(c->k==3, "Spline1DRootsAndExtrema : incorrect parameter C.K!", _state);
|
|
ae_assert(c->continuity>=0, "Spline1DRootsAndExtrema : parameter C.Continuity must not be less than 0!", _state);
|
|
|
|
/*
|
|
*initialization of variable
|
|
*/
|
|
*nr = 0;
|
|
*ne = 0;
|
|
*dr = ae_false;
|
|
*de = ae_false;
|
|
nstep = ae_true;
|
|
|
|
/*
|
|
*consider case, when C.Continuty=0
|
|
*/
|
|
if( c->continuity==0 )
|
|
{
|
|
|
|
/*
|
|
*allocation for auxiliary arrays
|
|
*'TmpR ' - it stores a time value for roots
|
|
*'TmpE ' - it stores a time value for extremums
|
|
*'TmpET '- it stores a time value for extremums type
|
|
*/
|
|
rvectorsetlengthatleast(&tmpr, 3*(c->n-1), _state);
|
|
rvectorsetlengthatleast(&tmpe, 2*(c->n-1), _state);
|
|
ivectorsetlengthatleast(&tmpet, 2*(c->n-1), _state);
|
|
|
|
/*
|
|
*start calculating
|
|
*/
|
|
for(i=0; i<=c->n-2; i++)
|
|
{
|
|
|
|
/*
|
|
*initialization pL, mL, pR, mR
|
|
*/
|
|
pl = c->c.ptr.p_double[4*i];
|
|
ml = c->c.ptr.p_double[4*i+1];
|
|
pr = c->c.ptr.p_double[4*(i+1)];
|
|
mr = c->c.ptr.p_double[4*i+1]+2*c->c.ptr.p_double[4*i+2]*(c->x.ptr.p_double[i+1]-c->x.ptr.p_double[i])+3*c->c.ptr.p_double[4*i+3]*(c->x.ptr.p_double[i+1]-c->x.ptr.p_double[i])*(c->x.ptr.p_double[i+1]-c->x.ptr.p_double[i]);
|
|
|
|
/*
|
|
*pre-searching roots and extremums
|
|
*/
|
|
solvecubicpolinom(pl, ml, pr, mr, c->x.ptr.p_double[i], c->x.ptr.p_double[i+1], &x0, &x1, &x2, &ex0, &ex1, &tnr, &tne, &tr, _state);
|
|
*dr = *dr||tnr==-1;
|
|
*de = *de||tne==-1;
|
|
|
|
/*
|
|
*searching of roots
|
|
*/
|
|
if( tnr==1&&nstep )
|
|
{
|
|
|
|
/*
|
|
*is there roots?
|
|
*/
|
|
if( *nr>0 )
|
|
{
|
|
|
|
/*
|
|
*is a next root equal a previous root?
|
|
*if is't, then write new root
|
|
*/
|
|
if( ae_fp_neq(x0,tmpr.ptr.p_double[*nr-1]) )
|
|
{
|
|
tmpr.ptr.p_double[*nr] = x0;
|
|
*nr = *nr+1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*write a first root
|
|
*/
|
|
tmpr.ptr.p_double[*nr] = x0;
|
|
*nr = *nr+1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*case when function at a segment identically to zero
|
|
*then we have to clear a root, if the one located on a
|
|
*constant segment
|
|
*/
|
|
if( tnr==-1 )
|
|
{
|
|
|
|
/*
|
|
*safe state variable as constant
|
|
*/
|
|
if( nstep )
|
|
{
|
|
nstep = ae_false;
|
|
}
|
|
|
|
/*
|
|
*clear the root, if there is
|
|
*/
|
|
if( *nr>0 )
|
|
{
|
|
if( ae_fp_eq(c->x.ptr.p_double[i],tmpr.ptr.p_double[*nr-1]) )
|
|
{
|
|
*nr = *nr-1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*change state for 'DR'
|
|
*/
|
|
if( !*dr )
|
|
{
|
|
*dr = ae_true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nstep = ae_true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*searching of extremums
|
|
*/
|
|
if( i>0 )
|
|
{
|
|
pll = c->c.ptr.p_double[4*(i-1)];
|
|
|
|
/*
|
|
*if pL=pLL or pL=pR then
|
|
*/
|
|
if( tne==-1 )
|
|
{
|
|
if( !*de )
|
|
{
|
|
*de = ae_true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater(pl,pll)&&ae_fp_greater(pl,pr) )
|
|
{
|
|
|
|
/*
|
|
*maximum
|
|
*/
|
|
tmpet.ptr.p_int[*ne] = -1;
|
|
tmpe.ptr.p_double[*ne] = c->x.ptr.p_double[i];
|
|
*ne = *ne+1;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_less(pl,pll)&&ae_fp_less(pl,pr) )
|
|
{
|
|
|
|
/*
|
|
*minimum
|
|
*/
|
|
tmpet.ptr.p_int[*ne] = 1;
|
|
tmpe.ptr.p_double[*ne] = c->x.ptr.p_double[i];
|
|
*ne = *ne+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*write final result
|
|
*/
|
|
rvectorsetlengthatleast(r, *nr, _state);
|
|
rvectorsetlengthatleast(e, *ne, _state);
|
|
ivectorsetlengthatleast(et, *ne, _state);
|
|
|
|
/*
|
|
*write roots
|
|
*/
|
|
for(i=0; i<=*nr-1; i++)
|
|
{
|
|
r->ptr.p_double[i] = tmpr.ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
*write extremums and their types
|
|
*/
|
|
for(i=0; i<=*ne-1; i++)
|
|
{
|
|
e->ptr.p_double[i] = tmpe.ptr.p_double[i];
|
|
et->ptr.p_int[i] = tmpet.ptr.p_int[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*case, when C.Continuity>=1
|
|
*'TmpR ' - it stores a time value for roots
|
|
*'TmpC' - it stores a time value for extremums and
|
|
*their function value (TmpC={EX0,F(EX0), EX1,F(EX1), ..., EXn,F(EXn)};)
|
|
*'TmpE' - it stores a time value for extremums only
|
|
*'TmpET'- it stores a time value for extremums type
|
|
*/
|
|
rvectorsetlengthatleast(&tmpr, 2*c->n-1, _state);
|
|
rvectorsetlengthatleast(&tmpc, 4*c->n, _state);
|
|
rvectorsetlengthatleast(&tmpe, 2*c->n, _state);
|
|
ivectorsetlengthatleast(&tmpet, 2*c->n, _state);
|
|
|
|
/*
|
|
*start calculating
|
|
*/
|
|
for(i=0; i<=c->n-2; i++)
|
|
{
|
|
|
|
/*
|
|
*we calculate pL,mL, pR,mR as Fi+1(F'i+1) at left border
|
|
*/
|
|
pl = c->c.ptr.p_double[4*i];
|
|
ml = c->c.ptr.p_double[4*i+1];
|
|
pr = c->c.ptr.p_double[4*(i+1)];
|
|
mr = c->c.ptr.p_double[4*(i+1)+1];
|
|
|
|
/*
|
|
*calculating roots and extremums at [X[i],X[i+1]]
|
|
*/
|
|
solvecubicpolinom(pl, ml, pr, mr, c->x.ptr.p_double[i], c->x.ptr.p_double[i+1], &x0, &x1, &x2, &ex0, &ex1, &tnr, &tne, &tr, _state);
|
|
|
|
/*
|
|
*searching roots
|
|
*/
|
|
if( tnr>0 )
|
|
{
|
|
|
|
/*
|
|
*re-init tR
|
|
*/
|
|
if( tnr>=1 )
|
|
{
|
|
tr.ptr.p_double[0] = x0;
|
|
}
|
|
if( tnr>=2 )
|
|
{
|
|
tr.ptr.p_double[1] = x1;
|
|
}
|
|
if( tnr==3 )
|
|
{
|
|
tr.ptr.p_double[2] = x2;
|
|
}
|
|
|
|
/*
|
|
*start root selection
|
|
*/
|
|
if( *nr>0 )
|
|
{
|
|
if( ae_fp_neq(tmpr.ptr.p_double[*nr-1],x0) )
|
|
{
|
|
|
|
/*
|
|
*previous segment was't constant identical zero
|
|
*/
|
|
if( nstep )
|
|
{
|
|
for(j=0; j<=tnr-1; j++)
|
|
{
|
|
tmpr.ptr.p_double[*nr+j] = tr.ptr.p_double[j];
|
|
}
|
|
*nr = *nr+tnr;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*previous segment was constant identical zero
|
|
*and we must ignore [NR+j-1] root
|
|
*/
|
|
for(j=1; j<=tnr-1; j++)
|
|
{
|
|
tmpr.ptr.p_double[*nr+j-1] = tr.ptr.p_double[j];
|
|
}
|
|
*nr = *nr+tnr-1;
|
|
nstep = ae_true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(j=1; j<=tnr-1; j++)
|
|
{
|
|
tmpr.ptr.p_double[*nr+j-1] = tr.ptr.p_double[j];
|
|
}
|
|
*nr = *nr+tnr-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*write first root
|
|
*/
|
|
for(j=0; j<=tnr-1; j++)
|
|
{
|
|
tmpr.ptr.p_double[*nr+j] = tr.ptr.p_double[j];
|
|
}
|
|
*nr = *nr+tnr;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( tnr==-1 )
|
|
{
|
|
|
|
/*
|
|
*decrement 'NR' if at previous step was writen a root
|
|
*(previous segment identical zero)
|
|
*/
|
|
if( *nr>0&&nstep )
|
|
{
|
|
*nr = *nr-1;
|
|
}
|
|
|
|
/*
|
|
*previous segment is't constant
|
|
*/
|
|
if( nstep )
|
|
{
|
|
nstep = ae_false;
|
|
}
|
|
|
|
/*
|
|
*rewrite 'DR'
|
|
*/
|
|
if( !*dr )
|
|
{
|
|
*dr = ae_true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*searching extremums
|
|
*write all term like extremums
|
|
*/
|
|
if( tne==1 )
|
|
{
|
|
if( *ne>0 )
|
|
{
|
|
|
|
/*
|
|
*just ignore identical extremums
|
|
*because he must be one
|
|
*/
|
|
if( ae_fp_neq(tmpc.ptr.p_double[*ne-2],ex0) )
|
|
{
|
|
tmpc.ptr.p_double[*ne] = ex0;
|
|
tmpc.ptr.p_double[*ne+1] = c->c.ptr.p_double[4*i]+c->c.ptr.p_double[4*i+1]*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+2]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+3]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i]);
|
|
*ne = *ne+2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*write first extremum and it function value
|
|
*/
|
|
tmpc.ptr.p_double[*ne] = ex0;
|
|
tmpc.ptr.p_double[*ne+1] = c->c.ptr.p_double[4*i]+c->c.ptr.p_double[4*i+1]*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+2]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+3]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i]);
|
|
*ne = *ne+2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( tne==2 )
|
|
{
|
|
if( *ne>0 )
|
|
{
|
|
|
|
/*
|
|
*ignore identical extremum
|
|
*/
|
|
if( ae_fp_neq(tmpc.ptr.p_double[*ne-2],ex0) )
|
|
{
|
|
tmpc.ptr.p_double[*ne] = ex0;
|
|
tmpc.ptr.p_double[*ne+1] = c->c.ptr.p_double[4*i]+c->c.ptr.p_double[4*i+1]*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+2]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+3]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i]);
|
|
*ne = *ne+2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*write first extremum
|
|
*/
|
|
tmpc.ptr.p_double[*ne] = ex0;
|
|
tmpc.ptr.p_double[*ne+1] = c->c.ptr.p_double[4*i]+c->c.ptr.p_double[4*i+1]*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+2]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+3]*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i])*(ex0-c->x.ptr.p_double[i]);
|
|
*ne = *ne+2;
|
|
}
|
|
|
|
/*
|
|
*write second extremum
|
|
*/
|
|
tmpc.ptr.p_double[*ne] = ex1;
|
|
tmpc.ptr.p_double[*ne+1] = c->c.ptr.p_double[4*i]+c->c.ptr.p_double[4*i+1]*(ex1-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+2]*(ex1-c->x.ptr.p_double[i])*(ex1-c->x.ptr.p_double[i])+c->c.ptr.p_double[4*i+3]*(ex1-c->x.ptr.p_double[i])*(ex1-c->x.ptr.p_double[i])*(ex1-c->x.ptr.p_double[i]);
|
|
*ne = *ne+2;
|
|
}
|
|
else
|
|
{
|
|
if( tne==-1 )
|
|
{
|
|
if( !*de )
|
|
{
|
|
*de = ae_true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*checking of arrays
|
|
*get number of extremums (tNe=NE/2)
|
|
*initialize pL as value F0(X[0]) and
|
|
*initialize pR as value Fn-1(X[N])
|
|
*/
|
|
tne = *ne/2;
|
|
*ne = 0;
|
|
pl = c->c.ptr.p_double[0];
|
|
pr = c->c.ptr.p_double[4*(c->n-1)];
|
|
for(i=0; i<=tne-1; i++)
|
|
{
|
|
if( i>0&&i<tne-1 )
|
|
{
|
|
if( ae_fp_greater(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i-1)+1])&&ae_fp_greater(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i+1)+1]) )
|
|
{
|
|
|
|
/*
|
|
*maximum
|
|
*/
|
|
tmpe.ptr.p_double[*ne] = tmpc.ptr.p_double[2*i];
|
|
tmpet.ptr.p_int[*ne] = -1;
|
|
*ne = *ne+1;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_less(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i-1)+1])&&ae_fp_less(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i+1)+1]) )
|
|
{
|
|
|
|
/*
|
|
*minimum
|
|
*/
|
|
tmpe.ptr.p_double[*ne] = tmpc.ptr.p_double[2*i];
|
|
tmpet.ptr.p_int[*ne] = 1;
|
|
*ne = *ne+1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( i==0 )
|
|
{
|
|
if( ae_fp_neq(tmpc.ptr.p_double[2*i],c->x.ptr.p_double[0]) )
|
|
{
|
|
if( ae_fp_greater(tmpc.ptr.p_double[2*i+1],pl)&&ae_fp_greater(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i+1)+1]) )
|
|
{
|
|
|
|
/*
|
|
*maximum
|
|
*/
|
|
tmpe.ptr.p_double[*ne] = tmpc.ptr.p_double[2*i];
|
|
tmpet.ptr.p_int[*ne] = -1;
|
|
*ne = *ne+1;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_less(tmpc.ptr.p_double[2*i+1],pl)&&ae_fp_less(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i+1)+1]) )
|
|
{
|
|
|
|
/*
|
|
*minimum
|
|
*/
|
|
tmpe.ptr.p_double[*ne] = tmpc.ptr.p_double[2*i];
|
|
tmpet.ptr.p_int[*ne] = 1;
|
|
*ne = *ne+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( i==tne-1 )
|
|
{
|
|
if( ae_fp_neq(tmpc.ptr.p_double[2*i],c->x.ptr.p_double[c->n-1]) )
|
|
{
|
|
if( ae_fp_greater(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i-1)+1])&&ae_fp_greater(tmpc.ptr.p_double[2*i+1],pr) )
|
|
{
|
|
|
|
/*
|
|
*maximum
|
|
*/
|
|
tmpe.ptr.p_double[*ne] = tmpc.ptr.p_double[2*i];
|
|
tmpet.ptr.p_int[*ne] = -1;
|
|
*ne = *ne+1;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_less(tmpc.ptr.p_double[2*i+1],tmpc.ptr.p_double[2*(i-1)+1])&&ae_fp_less(tmpc.ptr.p_double[2*i+1],pr) )
|
|
{
|
|
|
|
/*
|
|
*minimum
|
|
*/
|
|
tmpe.ptr.p_double[*ne] = tmpc.ptr.p_double[2*i];
|
|
tmpet.ptr.p_int[*ne] = 1;
|
|
*ne = *ne+1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*final results
|
|
*allocate R, E, ET
|
|
*/
|
|
rvectorsetlengthatleast(r, *nr, _state);
|
|
rvectorsetlengthatleast(e, *ne, _state);
|
|
ivectorsetlengthatleast(et, *ne, _state);
|
|
|
|
/*
|
|
*write result for extremus and their types
|
|
*/
|
|
for(i=0; i<=*ne-1; i++)
|
|
{
|
|
e->ptr.p_double[i] = tmpe.ptr.p_double[i];
|
|
et->ptr.p_int[i] = tmpet.ptr.p_int[i];
|
|
}
|
|
|
|
/*
|
|
*write result for roots
|
|
*/
|
|
for(i=0; i<=*nr-1; i++)
|
|
{
|
|
r->ptr.p_double[i] = tmpr.ptr.p_double[i];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine. Heap sort.
|
|
*************************************************************************/
|
|
void heapsortdpoints(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* d,
|
|
ae_int_t n,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector rbuf;
|
|
ae_vector ibuf;
|
|
ae_vector rbuf2;
|
|
ae_vector ibuf2;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&rbuf, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ibuf, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&rbuf2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ibuf2, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_vector_set_length(&ibuf, n, _state);
|
|
ae_vector_set_length(&rbuf, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ibuf.ptr.p_int[i] = i;
|
|
}
|
|
tagsortfasti(x, &ibuf, &rbuf2, &ibuf2, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
rbuf.ptr.p_double[i] = y->ptr.p_double[ibuf.ptr.p_int[i]];
|
|
}
|
|
ae_v_move(&y->ptr.p_double[0], 1, &rbuf.ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
rbuf.ptr.p_double[i] = d->ptr.p_double[ibuf.ptr.p_int[i]];
|
|
}
|
|
ae_v_move(&d->ptr.p_double[0], 1, &rbuf.ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This procedure search roots of an quadratic equation inside [0;1] and it number of roots.
|
|
|
|
INPUT PARAMETERS:
|
|
P0 - value of a function at 0
|
|
M0 - value of a derivative at 0
|
|
P1 - value of a function at 1
|
|
M1 - value of a derivative at 1
|
|
|
|
OUTPUT PARAMETERS:
|
|
X0 - first root of an equation
|
|
X1 - second root of an equation
|
|
NR - number of roots
|
|
|
|
RESTRICTIONS OF PARAMETERS:
|
|
|
|
Parameters for this procedure has't to be zero simultaneously. Is expected,
|
|
that input polinom is't degenerate or constant identicaly ZERO.
|
|
|
|
|
|
REMARK:
|
|
|
|
The procedure always fill value for X1 and X2, even if it is't belongs to [0;1].
|
|
But first true root(even if existing one) is in X1.
|
|
Number of roots is NR.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.09.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void solvepolinom2(double p0,
|
|
double m0,
|
|
double p1,
|
|
double m1,
|
|
double* x0,
|
|
double* x1,
|
|
ae_int_t* nr,
|
|
ae_state *_state)
|
|
{
|
|
double a;
|
|
double b;
|
|
double c;
|
|
double dd;
|
|
double tmp;
|
|
double exf;
|
|
double extr;
|
|
|
|
*x0 = 0;
|
|
*x1 = 0;
|
|
*nr = 0;
|
|
|
|
|
|
/*
|
|
*calculate parameters for equation: A, B and C
|
|
*/
|
|
a = 6*p0+3*m0-6*p1+3*m1;
|
|
b = -6*p0-4*m0+6*p1-2*m1;
|
|
c = m0;
|
|
|
|
/*
|
|
*check case, when A=0
|
|
*we are considering the linear equation
|
|
*/
|
|
if( ae_fp_eq(a,0) )
|
|
{
|
|
|
|
/*
|
|
*B<>0 and root inside [0;1]
|
|
*one root
|
|
*/
|
|
if( (ae_fp_neq(b,0)&&ae_sign(c, _state)*ae_sign(b, _state)<=0)&&ae_fp_greater_eq(ae_fabs(b, _state),ae_fabs(c, _state)) )
|
|
{
|
|
*x0 = -c/b;
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
*nr = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*consider case, when extremumu outside (0;1)
|
|
*exist one root only
|
|
*/
|
|
if( ae_fp_less_eq(ae_fabs(2*a, _state),ae_fabs(b, _state))||ae_sign(b, _state)*ae_sign(a, _state)>=0 )
|
|
{
|
|
if( ae_sign(m0, _state)*ae_sign(m1, _state)>0 )
|
|
{
|
|
*nr = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*consider case, when the one exist
|
|
*same sign of derivative
|
|
*/
|
|
if( ae_sign(m0, _state)*ae_sign(m1, _state)<0 )
|
|
{
|
|
*nr = 1;
|
|
extr = -b/(2*a);
|
|
dd = b*b-4*a*c;
|
|
if( ae_fp_less(dd,0) )
|
|
{
|
|
return;
|
|
}
|
|
*x0 = (-b-ae_sqrt(dd, _state))/(2*a);
|
|
*x1 = (-b+ae_sqrt(dd, _state))/(2*a);
|
|
if( (ae_fp_greater_eq(extr,1)&&ae_fp_less_eq(*x1,extr))||(ae_fp_less_eq(extr,0)&&ae_fp_greater_eq(*x1,extr)) )
|
|
{
|
|
*x0 = *x1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*consider case, when the one is 0
|
|
*/
|
|
if( ae_fp_eq(m0,0) )
|
|
{
|
|
*x0 = 0;
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
if( ae_fp_eq(m1,0) )
|
|
{
|
|
*x0 = 1;
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*consider case, when both of derivatives is 0
|
|
*/
|
|
if( ae_fp_eq(m0,0)&&ae_fp_eq(m1,0) )
|
|
{
|
|
*x0 = 0;
|
|
*x1 = 1;
|
|
*nr = 2;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*consider case, when derivative at 0 is 0, and derivative at 1 is't 0
|
|
*/
|
|
if( ae_fp_eq(m0,0)&&ae_fp_neq(m1,0) )
|
|
{
|
|
dd = b*b-4*a*c;
|
|
if( ae_fp_less(dd,0) )
|
|
{
|
|
*x0 = 0;
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
*x0 = (-b-ae_sqrt(dd, _state))/(2*a);
|
|
*x1 = (-b+ae_sqrt(dd, _state))/(2*a);
|
|
extr = -b/(2*a);
|
|
exf = a*extr*extr+b*extr+c;
|
|
if( ae_sign(exf, _state)*ae_sign(m1, _state)>0 )
|
|
{
|
|
*x0 = 0;
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater(extr,*x0) )
|
|
{
|
|
*x0 = 0;
|
|
}
|
|
else
|
|
{
|
|
*x1 = 0;
|
|
}
|
|
*nr = 2;
|
|
|
|
/*
|
|
*roots must placed ascending
|
|
*/
|
|
if( ae_fp_greater(*x0,*x1) )
|
|
{
|
|
tmp = *x0;
|
|
*x0 = *x1;
|
|
*x1 = tmp;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if( ae_fp_eq(m1,0)&&ae_fp_neq(m0,0) )
|
|
{
|
|
dd = b*b-4*a*c;
|
|
if( ae_fp_less(dd,0) )
|
|
{
|
|
*x0 = 1;
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
*x0 = (-b-ae_sqrt(dd, _state))/(2*a);
|
|
*x1 = (-b+ae_sqrt(dd, _state))/(2*a);
|
|
extr = -b/(2*a);
|
|
exf = a*extr*extr+b*extr+c;
|
|
if( ae_sign(exf, _state)*ae_sign(m0, _state)>0 )
|
|
{
|
|
*x0 = 1;
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_less(extr,*x0) )
|
|
{
|
|
*x0 = 1;
|
|
}
|
|
else
|
|
{
|
|
*x1 = 1;
|
|
}
|
|
*nr = 2;
|
|
|
|
/*
|
|
*roots must placed ascending
|
|
*/
|
|
if( ae_fp_greater(*x0,*x1) )
|
|
{
|
|
tmp = *x0;
|
|
*x0 = *x1;
|
|
*x1 = tmp;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
extr = -b/(2*a);
|
|
exf = a*extr*extr+b*extr+c;
|
|
if( ae_sign(exf, _state)*ae_sign(m0, _state)>0&&ae_sign(exf, _state)*ae_sign(m1, _state)>0 )
|
|
{
|
|
*nr = 0;
|
|
return;
|
|
}
|
|
dd = b*b-4*a*c;
|
|
if( ae_fp_less(dd,0) )
|
|
{
|
|
*nr = 0;
|
|
return;
|
|
}
|
|
*x0 = (-b-ae_sqrt(dd, _state))/(2*a);
|
|
*x1 = (-b+ae_sqrt(dd, _state))/(2*a);
|
|
|
|
/*
|
|
*if EXF and m0, EXF and m1 has different signs, then equation has two roots
|
|
*/
|
|
if( ae_sign(exf, _state)*ae_sign(m0, _state)<0&&ae_sign(exf, _state)*ae_sign(m1, _state)<0 )
|
|
{
|
|
*nr = 2;
|
|
|
|
/*
|
|
*roots must placed ascending
|
|
*/
|
|
if( ae_fp_greater(*x0,*x1) )
|
|
{
|
|
tmp = *x0;
|
|
*x0 = *x1;
|
|
*x1 = tmp;
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
*nr = 1;
|
|
if( ae_sign(exf, _state)*ae_sign(m0, _state)<0 )
|
|
{
|
|
if( ae_fp_less(*x1,extr) )
|
|
{
|
|
*x0 = *x1;
|
|
}
|
|
return;
|
|
}
|
|
if( ae_sign(exf, _state)*ae_sign(m1, _state)<0 )
|
|
{
|
|
if( ae_fp_greater(*x1,extr) )
|
|
{
|
|
*x0 = *x1;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This procedure search roots of an cubic equation inside [A;B], it number of roots
|
|
and number of extremums.
|
|
|
|
INPUT PARAMETERS:
|
|
pA - value of a function at A
|
|
mA - value of a derivative at A
|
|
pB - value of a function at B
|
|
mB - value of a derivative at B
|
|
A0 - left border [A0;B0]
|
|
B0 - right border [A0;B0]
|
|
|
|
OUTPUT PARAMETERS:
|
|
X0 - first root of an equation
|
|
X1 - second root of an equation
|
|
X2 - third root of an equation
|
|
EX0 - first extremum of a function
|
|
EX0 - second extremum of a function
|
|
NR - number of roots
|
|
NR - number of extrmums
|
|
|
|
RESTRICTIONS OF PARAMETERS:
|
|
|
|
Length of [A;B] must be positive and is't zero, i.e. A<>B and A<B.
|
|
|
|
|
|
REMARK:
|
|
|
|
If 'NR' is -1 it's mean, than polinom has infiniti roots.
|
|
If 'NE' is -1 it's mean, than polinom has infiniti extremums.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.09.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void solvecubicpolinom(double pa,
|
|
double ma,
|
|
double pb,
|
|
double mb,
|
|
double a,
|
|
double b,
|
|
double* x0,
|
|
double* x1,
|
|
double* x2,
|
|
double* ex0,
|
|
double* ex1,
|
|
ae_int_t* nr,
|
|
ae_int_t* ne,
|
|
/* Real */ ae_vector* tempdata,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
double tmpma;
|
|
double tmpmb;
|
|
double tex0;
|
|
double tex1;
|
|
|
|
*x0 = 0;
|
|
*x1 = 0;
|
|
*x2 = 0;
|
|
*ex0 = 0;
|
|
*ex1 = 0;
|
|
*nr = 0;
|
|
*ne = 0;
|
|
|
|
rvectorsetlengthatleast(tempdata, 3, _state);
|
|
|
|
/*
|
|
*case, when A>B
|
|
*/
|
|
ae_assert(ae_fp_less(a,b), "\nSolveCubicPolinom: incorrect borders for [A;B]!\n", _state);
|
|
|
|
/*
|
|
*case 1
|
|
*function can be identicaly to ZERO
|
|
*/
|
|
if( ((ae_fp_eq(ma,0)&&ae_fp_eq(mb,0))&&ae_fp_eq(pa,pb))&&ae_fp_eq(pa,0) )
|
|
{
|
|
*nr = -1;
|
|
*ne = -1;
|
|
return;
|
|
}
|
|
if( (ae_fp_eq(ma,0)&&ae_fp_eq(mb,0))&&ae_fp_eq(pa,pb) )
|
|
{
|
|
*nr = 0;
|
|
*ne = -1;
|
|
return;
|
|
}
|
|
tmpma = ma*(b-a);
|
|
tmpmb = mb*(b-a);
|
|
solvepolinom2(pa, tmpma, pb, tmpmb, ex0, ex1, ne, _state);
|
|
*ex0 = spline1d_rescaleval(0, 1, a, b, *ex0, _state);
|
|
*ex1 = spline1d_rescaleval(0, 1, a, b, *ex1, _state);
|
|
|
|
/*
|
|
*case 3.1
|
|
*no extremums at [A;B]
|
|
*/
|
|
if( *ne==0 )
|
|
{
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, 0, 1, x0, _state);
|
|
if( *nr==1 )
|
|
{
|
|
*x0 = spline1d_rescaleval(0, 1, a, b, *x0, _state);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*case 3.2
|
|
*one extremum
|
|
*/
|
|
if( *ne==1 )
|
|
{
|
|
if( ae_fp_eq(*ex0,a)||ae_fp_eq(*ex0,b) )
|
|
{
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, 0, 1, x0, _state);
|
|
if( *nr==1 )
|
|
{
|
|
*x0 = spline1d_rescaleval(0, 1, a, b, *x0, _state);
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
*nr = 0;
|
|
i = 0;
|
|
tex0 = spline1d_rescaleval(a, b, 0, 1, *ex0, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, 0, tex0, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval(0, tex0, a, *ex0, *x0, _state);
|
|
i = i+1;
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex0, 1, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex0, 1, *ex0, b, *x0, _state);
|
|
if( i>0 )
|
|
{
|
|
if( ae_fp_neq(*x0,tempdata->ptr.p_double[i-1]) )
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
else
|
|
{
|
|
*nr = *nr-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
}
|
|
if( *nr>0 )
|
|
{
|
|
*x0 = tempdata->ptr.p_double[0];
|
|
if( *nr>1 )
|
|
{
|
|
*x1 = tempdata->ptr.p_double[1];
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*case 3.3
|
|
*two extremums(or more, but it's impossible)
|
|
*
|
|
*
|
|
*case 3.3.0
|
|
*both extremums at the border
|
|
*/
|
|
if( ae_fp_eq(*ex0,a)&&ae_fp_eq(*ex1,b) )
|
|
{
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, 0, 1, x0, _state);
|
|
if( *nr==1 )
|
|
{
|
|
*x0 = spline1d_rescaleval(0, 1, a, b, *x0, _state);
|
|
}
|
|
return;
|
|
}
|
|
if( ae_fp_eq(*ex0,a)&&ae_fp_neq(*ex1,b) )
|
|
{
|
|
*nr = 0;
|
|
i = 0;
|
|
tex1 = spline1d_rescaleval(a, b, 0, 1, *ex1, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, 0, tex1, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval(0, tex1, a, *ex1, *x0, _state);
|
|
i = i+1;
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex1, 1, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex1, 1, *ex1, b, *x0, _state);
|
|
if( ae_fp_neq(*x0,tempdata->ptr.p_double[i-1]) )
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
else
|
|
{
|
|
*nr = *nr-1;
|
|
}
|
|
}
|
|
if( *nr>0 )
|
|
{
|
|
*x0 = tempdata->ptr.p_double[0];
|
|
if( *nr>1 )
|
|
{
|
|
*x1 = tempdata->ptr.p_double[1];
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if( ae_fp_eq(*ex1,b)&&ae_fp_neq(*ex0,a) )
|
|
{
|
|
*nr = 0;
|
|
i = 0;
|
|
tex0 = spline1d_rescaleval(a, b, 0, 1, *ex0, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, 0, tex0, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval(0, tex0, a, *ex0, *x0, _state);
|
|
i = i+1;
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex0, 1, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex0, 1, *ex0, b, *x0, _state);
|
|
if( i>0 )
|
|
{
|
|
if( ae_fp_neq(*x0,tempdata->ptr.p_double[i-1]) )
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
else
|
|
{
|
|
*nr = *nr-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
}
|
|
if( *nr>0 )
|
|
{
|
|
*x0 = tempdata->ptr.p_double[0];
|
|
if( *nr>1 )
|
|
{
|
|
*x1 = tempdata->ptr.p_double[1];
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*case 3.3.2
|
|
*both extremums inside (0;1)
|
|
*/
|
|
*nr = 0;
|
|
i = 0;
|
|
tex0 = spline1d_rescaleval(a, b, 0, 1, *ex0, _state);
|
|
tex1 = spline1d_rescaleval(a, b, 0, 1, *ex1, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, 0, tex0, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval(0, tex0, a, *ex0, *x0, _state);
|
|
i = i+1;
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex0, tex1, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex0, tex1, *ex0, *ex1, *x0, _state);
|
|
if( i>0 )
|
|
{
|
|
if( ae_fp_neq(*x0,tempdata->ptr.p_double[i-1]) )
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
else
|
|
{
|
|
*nr = *nr-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex1, 1, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex1, 1, *ex1, b, *x0, _state);
|
|
if( i>0 )
|
|
{
|
|
if( ae_fp_neq(*x0,tempdata->ptr.p_double[i-1]) )
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
else
|
|
{
|
|
*nr = *nr-1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tempdata->ptr.p_double[i] = *x0;
|
|
i = i+1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*write are found roots
|
|
*/
|
|
if( *nr>0 )
|
|
{
|
|
*x0 = tempdata->ptr.p_double[0];
|
|
if( *nr>1 )
|
|
{
|
|
*x1 = tempdata->ptr.p_double[1];
|
|
}
|
|
if( *nr>2 )
|
|
{
|
|
*x2 = tempdata->ptr.p_double[2];
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Function for searching a root at [A;B] by bisection method and return number of roots
|
|
(0 or 1)
|
|
|
|
INPUT PARAMETERS:
|
|
pA - value of a function at A
|
|
mA - value of a derivative at A
|
|
pB - value of a function at B
|
|
mB - value of a derivative at B
|
|
A0 - left border [A0;B0]
|
|
B0 - right border [A0;B0]
|
|
|
|
RESTRICTIONS OF PARAMETERS:
|
|
|
|
We assume, that B0>A0.
|
|
|
|
|
|
REMARK:
|
|
|
|
Assume, that exist one root only at [A;B], else
|
|
function may be work incorrectly.
|
|
The function dont check value A0,B0!
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.09.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
ae_int_t bisectmethod(double pa,
|
|
double ma,
|
|
double pb,
|
|
double mb,
|
|
double a,
|
|
double b,
|
|
double* x,
|
|
ae_state *_state)
|
|
{
|
|
double vacuum;
|
|
double eps;
|
|
double a0;
|
|
double b0;
|
|
double m;
|
|
double lf;
|
|
double rf;
|
|
double mf;
|
|
ae_int_t result;
|
|
|
|
*x = 0;
|
|
|
|
|
|
/*
|
|
*accuracy
|
|
*/
|
|
eps = 1000*(b-a)*ae_machineepsilon;
|
|
|
|
/*
|
|
*initialization left and right borders
|
|
*/
|
|
a0 = a;
|
|
b0 = b;
|
|
|
|
/*
|
|
*initialize function value at 'A' and 'B'
|
|
*/
|
|
spline1d_hermitecalc(pa, ma, pb, mb, a, &lf, &vacuum, _state);
|
|
spline1d_hermitecalc(pa, ma, pb, mb, b, &rf, &vacuum, _state);
|
|
|
|
/*
|
|
*check, that 'A' and 'B' are't roots,
|
|
*and that root exist
|
|
*/
|
|
if( ae_sign(lf, _state)*ae_sign(rf, _state)>0 )
|
|
{
|
|
result = 0;
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_eq(lf,0) )
|
|
{
|
|
*x = a;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_eq(rf,0) )
|
|
{
|
|
*x = b;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
*searching a root
|
|
*/
|
|
do
|
|
{
|
|
m = (b0+a0)/2;
|
|
spline1d_hermitecalc(pa, ma, pb, mb, a0, &lf, &vacuum, _state);
|
|
spline1d_hermitecalc(pa, ma, pb, mb, b0, &rf, &vacuum, _state);
|
|
spline1d_hermitecalc(pa, ma, pb, mb, m, &mf, &vacuum, _state);
|
|
if( ae_sign(mf, _state)*ae_sign(lf, _state)<0 )
|
|
{
|
|
b0 = m;
|
|
}
|
|
else
|
|
{
|
|
if( ae_sign(mf, _state)*ae_sign(rf, _state)<0 )
|
|
{
|
|
a0 = m;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_eq(lf,0) )
|
|
{
|
|
*x = a0;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
if( ae_fp_eq(rf,0) )
|
|
{
|
|
*x = b0;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
if( ae_fp_eq(mf,0) )
|
|
{
|
|
*x = m;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(ae_fp_greater_eq(ae_fabs(b0-a0, _state),eps));
|
|
*x = m;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function builds monotone cubic Hermite interpolant. This interpolant
|
|
is monotonic in [x(0),x(n-1)] and is constant outside of this interval.
|
|
|
|
In case y[] form non-monotonic sequence, interpolant is piecewise
|
|
monotonic. Say, for x=(0,1,2,3,4) and y=(0,1,2,1,0) interpolant will
|
|
monotonically grow at [0..2] and monotonically decrease at [2..4].
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline nodes, array[0..N-1]. Subroutine automatically
|
|
sorts points, so caller may pass unsorted array.
|
|
Y - function values, array[0..N-1]
|
|
N - the number of points(N>=2).
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 21.06.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dbuildmonotone(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
spline1dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector d;
|
|
ae_vector ex;
|
|
ae_vector ey;
|
|
ae_vector p;
|
|
double delta;
|
|
double alpha;
|
|
double beta;
|
|
ae_int_t tmpn;
|
|
ae_int_t sn;
|
|
double ca;
|
|
double cb;
|
|
double epsilon;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
_spline1dinterpolant_clear(c);
|
|
ae_vector_init(&d, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ex, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ey, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Check lengths of arguments
|
|
*/
|
|
ae_assert(n>=2, "Spline1DBuildMonotone: N<2", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DBuildMonotone: Length(X)<N", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DBuildMonotone: Length(Y)<N", _state);
|
|
|
|
/*
|
|
* Check and sort points
|
|
*/
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DBuildMonotone: X contains infinite or NAN values", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DBuildMonotone: Y contains infinite or NAN values", _state);
|
|
spline1d_heapsortppoints(x, y, &p, n, _state);
|
|
ae_assert(aredistinct(x, n, _state), "Spline1DBuildMonotone: at least two consequent points are too close", _state);
|
|
epsilon = ae_machineepsilon;
|
|
n = n+2;
|
|
ae_vector_set_length(&d, n, _state);
|
|
ae_vector_set_length(&ex, n, _state);
|
|
ae_vector_set_length(&ey, n, _state);
|
|
ex.ptr.p_double[0] = x->ptr.p_double[0]-ae_fabs(x->ptr.p_double[1]-x->ptr.p_double[0], _state);
|
|
ex.ptr.p_double[n-1] = x->ptr.p_double[n-3]+ae_fabs(x->ptr.p_double[n-3]-x->ptr.p_double[n-4], _state);
|
|
ey.ptr.p_double[0] = y->ptr.p_double[0];
|
|
ey.ptr.p_double[n-1] = y->ptr.p_double[n-3];
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
ex.ptr.p_double[i] = x->ptr.p_double[i-1];
|
|
ey.ptr.p_double[i] = y->ptr.p_double[i-1];
|
|
}
|
|
|
|
/*
|
|
* Init sign of the function for first segment
|
|
*/
|
|
i = 0;
|
|
ca = 0;
|
|
do
|
|
{
|
|
ca = ey.ptr.p_double[i+1]-ey.ptr.p_double[i];
|
|
i = i+1;
|
|
}
|
|
while(!(ae_fp_neq(ca,0)||i>n-2));
|
|
if( ae_fp_neq(ca,0) )
|
|
{
|
|
ca = ca/ae_fabs(ca, _state);
|
|
}
|
|
i = 0;
|
|
while(i<n-1)
|
|
{
|
|
|
|
/*
|
|
* Partition of the segment [X0;Xn]
|
|
*/
|
|
tmpn = 1;
|
|
for(j=i; j<=n-2; j++)
|
|
{
|
|
cb = ey.ptr.p_double[j+1]-ey.ptr.p_double[j];
|
|
if( ae_fp_greater_eq(ca*cb,0) )
|
|
{
|
|
tmpn = tmpn+1;
|
|
}
|
|
else
|
|
{
|
|
ca = cb/ae_fabs(cb, _state);
|
|
break;
|
|
}
|
|
}
|
|
sn = i+tmpn;
|
|
ae_assert(tmpn>=2, "Spline1DBuildMonotone: internal error", _state);
|
|
|
|
/*
|
|
* Calculate derivatives for current segment
|
|
*/
|
|
d.ptr.p_double[i] = 0;
|
|
d.ptr.p_double[sn-1] = 0;
|
|
for(j=i+1; j<=sn-2; j++)
|
|
{
|
|
d.ptr.p_double[j] = ((ey.ptr.p_double[j]-ey.ptr.p_double[j-1])/(ex.ptr.p_double[j]-ex.ptr.p_double[j-1])+(ey.ptr.p_double[j+1]-ey.ptr.p_double[j])/(ex.ptr.p_double[j+1]-ex.ptr.p_double[j]))/2;
|
|
}
|
|
for(j=i; j<=sn-2; j++)
|
|
{
|
|
delta = (ey.ptr.p_double[j+1]-ey.ptr.p_double[j])/(ex.ptr.p_double[j+1]-ex.ptr.p_double[j]);
|
|
if( ae_fp_less_eq(ae_fabs(delta, _state),epsilon) )
|
|
{
|
|
d.ptr.p_double[j] = 0;
|
|
d.ptr.p_double[j+1] = 0;
|
|
}
|
|
else
|
|
{
|
|
alpha = d.ptr.p_double[j]/delta;
|
|
beta = d.ptr.p_double[j+1]/delta;
|
|
if( ae_fp_neq(alpha,0) )
|
|
{
|
|
cb = alpha*ae_sqrt(1+ae_sqr(beta/alpha, _state), _state);
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_neq(beta,0) )
|
|
{
|
|
cb = beta;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if( ae_fp_greater(cb,3) )
|
|
{
|
|
d.ptr.p_double[j] = 3*alpha*delta/cb;
|
|
d.ptr.p_double[j+1] = 3*beta*delta/cb;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Transition to next segment
|
|
*/
|
|
i = sn-1;
|
|
}
|
|
spline1dbuildhermite(&ex, &ey, &d, n, c, _state);
|
|
c->continuity = 2;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal version of Spline1DGridDiffCubic.
|
|
|
|
Accepts pre-ordered X/Y, temporary arrays (which may be preallocated, if
|
|
you want to save time, or not) and output array (which may be preallocated
|
|
too).
|
|
|
|
Y is passed as var-parameter because we may need to force last element to
|
|
be equal to the first one (if periodic boundary conditions are specified).
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 03.09.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline1d_spline1dgriddiffcubicinternal(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t boundltype,
|
|
double boundl,
|
|
ae_int_t boundrtype,
|
|
double boundr,
|
|
/* Real */ ae_vector* d,
|
|
/* Real */ ae_vector* a1,
|
|
/* Real */ ae_vector* a2,
|
|
/* Real */ ae_vector* a3,
|
|
/* Real */ ae_vector* b,
|
|
/* Real */ ae_vector* dt,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
|
|
|
|
/*
|
|
* allocate arrays
|
|
*/
|
|
if( d->cnt<n )
|
|
{
|
|
ae_vector_set_length(d, n, _state);
|
|
}
|
|
if( a1->cnt<n )
|
|
{
|
|
ae_vector_set_length(a1, n, _state);
|
|
}
|
|
if( a2->cnt<n )
|
|
{
|
|
ae_vector_set_length(a2, n, _state);
|
|
}
|
|
if( a3->cnt<n )
|
|
{
|
|
ae_vector_set_length(a3, n, _state);
|
|
}
|
|
if( b->cnt<n )
|
|
{
|
|
ae_vector_set_length(b, n, _state);
|
|
}
|
|
if( dt->cnt<n )
|
|
{
|
|
ae_vector_set_length(dt, n, _state);
|
|
}
|
|
|
|
/*
|
|
* Special cases:
|
|
* * N=2, parabolic terminated boundary condition on both ends
|
|
* * N=2, periodic boundary condition
|
|
*/
|
|
if( (n==2&&boundltype==0)&&boundrtype==0 )
|
|
{
|
|
d->ptr.p_double[0] = (y->ptr.p_double[1]-y->ptr.p_double[0])/(x->ptr.p_double[1]-x->ptr.p_double[0]);
|
|
d->ptr.p_double[1] = d->ptr.p_double[0];
|
|
return;
|
|
}
|
|
if( (n==2&&boundltype==-1)&&boundrtype==-1 )
|
|
{
|
|
d->ptr.p_double[0] = 0;
|
|
d->ptr.p_double[1] = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Periodic and non-periodic boundary conditions are
|
|
* two separate classes
|
|
*/
|
|
if( boundrtype==-1&&boundltype==-1 )
|
|
{
|
|
|
|
/*
|
|
* Periodic boundary conditions
|
|
*/
|
|
y->ptr.p_double[n-1] = y->ptr.p_double[0];
|
|
|
|
/*
|
|
* Boundary conditions at N-1 points
|
|
* (one point less because last point is the same as first point).
|
|
*/
|
|
a1->ptr.p_double[0] = x->ptr.p_double[1]-x->ptr.p_double[0];
|
|
a2->ptr.p_double[0] = 2*(x->ptr.p_double[1]-x->ptr.p_double[0]+x->ptr.p_double[n-1]-x->ptr.p_double[n-2]);
|
|
a3->ptr.p_double[0] = x->ptr.p_double[n-1]-x->ptr.p_double[n-2];
|
|
b->ptr.p_double[0] = 3*(y->ptr.p_double[n-1]-y->ptr.p_double[n-2])/(x->ptr.p_double[n-1]-x->ptr.p_double[n-2])*(x->ptr.p_double[1]-x->ptr.p_double[0])+3*(y->ptr.p_double[1]-y->ptr.p_double[0])/(x->ptr.p_double[1]-x->ptr.p_double[0])*(x->ptr.p_double[n-1]-x->ptr.p_double[n-2]);
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
|
|
/*
|
|
* Altough last point is [N-2], we use X[N-1] and Y[N-1]
|
|
* (because of periodicity)
|
|
*/
|
|
a1->ptr.p_double[i] = x->ptr.p_double[i+1]-x->ptr.p_double[i];
|
|
a2->ptr.p_double[i] = 2*(x->ptr.p_double[i+1]-x->ptr.p_double[i-1]);
|
|
a3->ptr.p_double[i] = x->ptr.p_double[i]-x->ptr.p_double[i-1];
|
|
b->ptr.p_double[i] = 3*(y->ptr.p_double[i]-y->ptr.p_double[i-1])/(x->ptr.p_double[i]-x->ptr.p_double[i-1])*(x->ptr.p_double[i+1]-x->ptr.p_double[i])+3*(y->ptr.p_double[i+1]-y->ptr.p_double[i])/(x->ptr.p_double[i+1]-x->ptr.p_double[i])*(x->ptr.p_double[i]-x->ptr.p_double[i-1]);
|
|
}
|
|
|
|
/*
|
|
* Solve, add last point (with index N-1)
|
|
*/
|
|
spline1d_solvecyclictridiagonal(a1, a2, a3, b, n-1, dt, _state);
|
|
ae_v_move(&d->ptr.p_double[0], 1, &dt->ptr.p_double[0], 1, ae_v_len(0,n-2));
|
|
d->ptr.p_double[n-1] = d->ptr.p_double[0];
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Non-periodic boundary condition.
|
|
* Left boundary conditions.
|
|
*/
|
|
if( boundltype==0 )
|
|
{
|
|
a1->ptr.p_double[0] = 0;
|
|
a2->ptr.p_double[0] = 1;
|
|
a3->ptr.p_double[0] = 1;
|
|
b->ptr.p_double[0] = 2*(y->ptr.p_double[1]-y->ptr.p_double[0])/(x->ptr.p_double[1]-x->ptr.p_double[0]);
|
|
}
|
|
if( boundltype==1 )
|
|
{
|
|
a1->ptr.p_double[0] = 0;
|
|
a2->ptr.p_double[0] = 1;
|
|
a3->ptr.p_double[0] = 0;
|
|
b->ptr.p_double[0] = boundl;
|
|
}
|
|
if( boundltype==2 )
|
|
{
|
|
a1->ptr.p_double[0] = 0;
|
|
a2->ptr.p_double[0] = 2;
|
|
a3->ptr.p_double[0] = 1;
|
|
b->ptr.p_double[0] = 3*(y->ptr.p_double[1]-y->ptr.p_double[0])/(x->ptr.p_double[1]-x->ptr.p_double[0])-0.5*boundl*(x->ptr.p_double[1]-x->ptr.p_double[0]);
|
|
}
|
|
|
|
/*
|
|
* Central conditions
|
|
*/
|
|
for(i=1; i<=n-2; i++)
|
|
{
|
|
a1->ptr.p_double[i] = x->ptr.p_double[i+1]-x->ptr.p_double[i];
|
|
a2->ptr.p_double[i] = 2*(x->ptr.p_double[i+1]-x->ptr.p_double[i-1]);
|
|
a3->ptr.p_double[i] = x->ptr.p_double[i]-x->ptr.p_double[i-1];
|
|
b->ptr.p_double[i] = 3*(y->ptr.p_double[i]-y->ptr.p_double[i-1])/(x->ptr.p_double[i]-x->ptr.p_double[i-1])*(x->ptr.p_double[i+1]-x->ptr.p_double[i])+3*(y->ptr.p_double[i+1]-y->ptr.p_double[i])/(x->ptr.p_double[i+1]-x->ptr.p_double[i])*(x->ptr.p_double[i]-x->ptr.p_double[i-1]);
|
|
}
|
|
|
|
/*
|
|
* Right boundary conditions
|
|
*/
|
|
if( boundrtype==0 )
|
|
{
|
|
a1->ptr.p_double[n-1] = 1;
|
|
a2->ptr.p_double[n-1] = 1;
|
|
a3->ptr.p_double[n-1] = 0;
|
|
b->ptr.p_double[n-1] = 2*(y->ptr.p_double[n-1]-y->ptr.p_double[n-2])/(x->ptr.p_double[n-1]-x->ptr.p_double[n-2]);
|
|
}
|
|
if( boundrtype==1 )
|
|
{
|
|
a1->ptr.p_double[n-1] = 0;
|
|
a2->ptr.p_double[n-1] = 1;
|
|
a3->ptr.p_double[n-1] = 0;
|
|
b->ptr.p_double[n-1] = boundr;
|
|
}
|
|
if( boundrtype==2 )
|
|
{
|
|
a1->ptr.p_double[n-1] = 1;
|
|
a2->ptr.p_double[n-1] = 2;
|
|
a3->ptr.p_double[n-1] = 0;
|
|
b->ptr.p_double[n-1] = 3*(y->ptr.p_double[n-1]-y->ptr.p_double[n-2])/(x->ptr.p_double[n-1]-x->ptr.p_double[n-2])+0.5*boundr*(x->ptr.p_double[n-1]-x->ptr.p_double[n-2]);
|
|
}
|
|
|
|
/*
|
|
* Solve
|
|
*/
|
|
spline1d_solvetridiagonal(a1, a2, a3, b, n, d, _state);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine. Heap sort.
|
|
*************************************************************************/
|
|
static void spline1d_heapsortpoints(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector bufx;
|
|
ae_vector bufy;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&bufx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bufy, 0, DT_REAL, _state, ae_true);
|
|
|
|
tagsortfastr(x, y, &bufx, &bufy, n, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine. Heap sort.
|
|
|
|
Accepts:
|
|
X, Y - points
|
|
P - empty or preallocated array
|
|
|
|
Returns:
|
|
X, Y - sorted by X
|
|
P - array of permutations; I-th position of output
|
|
arrays X/Y contains (X[P[I]],Y[P[I]])
|
|
*************************************************************************/
|
|
static void spline1d_heapsortppoints(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Integer */ ae_vector* p,
|
|
ae_int_t n,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector rbuf;
|
|
ae_vector ibuf;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&rbuf, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ibuf, 0, DT_INT, _state, ae_true);
|
|
|
|
if( p->cnt<n )
|
|
{
|
|
ae_vector_set_length(p, n, _state);
|
|
}
|
|
ae_vector_set_length(&rbuf, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
p->ptr.p_int[i] = i;
|
|
}
|
|
tagsortfasti(x, p, &rbuf, &ibuf, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
rbuf.ptr.p_double[i] = y->ptr.p_double[p->ptr.p_int[i]];
|
|
}
|
|
ae_v_move(&y->ptr.p_double[0], 1, &rbuf.ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine. Tridiagonal solver. Solves
|
|
|
|
( B[0] C[0]
|
|
( A[1] B[1] C[1] )
|
|
( A[2] B[2] C[2] )
|
|
( .......... ) * X = D
|
|
( .......... )
|
|
( A[N-2] B[N-2] C[N-2] )
|
|
( A[N-1] B[N-1] )
|
|
|
|
*************************************************************************/
|
|
static void spline1d_solvetridiagonal(/* Real */ ae_vector* a,
|
|
/* Real */ ae_vector* b,
|
|
/* Real */ ae_vector* c,
|
|
/* Real */ ae_vector* d,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* x,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _b;
|
|
ae_vector _d;
|
|
ae_int_t k;
|
|
double t;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_b, b, _state, ae_true);
|
|
b = &_b;
|
|
ae_vector_init_copy(&_d, d, _state, ae_true);
|
|
d = &_d;
|
|
|
|
if( x->cnt<n )
|
|
{
|
|
ae_vector_set_length(x, n, _state);
|
|
}
|
|
for(k=1; k<=n-1; k++)
|
|
{
|
|
t = a->ptr.p_double[k]/b->ptr.p_double[k-1];
|
|
b->ptr.p_double[k] = b->ptr.p_double[k]-t*c->ptr.p_double[k-1];
|
|
d->ptr.p_double[k] = d->ptr.p_double[k]-t*d->ptr.p_double[k-1];
|
|
}
|
|
x->ptr.p_double[n-1] = d->ptr.p_double[n-1]/b->ptr.p_double[n-1];
|
|
for(k=n-2; k>=0; k--)
|
|
{
|
|
x->ptr.p_double[k] = (d->ptr.p_double[k]-c->ptr.p_double[k]*x->ptr.p_double[k+1])/b->ptr.p_double[k];
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine. Cyclic tridiagonal solver. Solves
|
|
|
|
( B[0] C[0] A[0] )
|
|
( A[1] B[1] C[1] )
|
|
( A[2] B[2] C[2] )
|
|
( .......... ) * X = D
|
|
( .......... )
|
|
( A[N-2] B[N-2] C[N-2] )
|
|
( C[N-1] A[N-1] B[N-1] )
|
|
*************************************************************************/
|
|
static void spline1d_solvecyclictridiagonal(/* Real */ ae_vector* a,
|
|
/* Real */ ae_vector* b,
|
|
/* Real */ ae_vector* c,
|
|
/* Real */ ae_vector* d,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* x,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _b;
|
|
ae_int_t k;
|
|
double alpha;
|
|
double beta;
|
|
double gamma;
|
|
ae_vector y;
|
|
ae_vector z;
|
|
ae_vector u;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_b, b, _state, ae_true);
|
|
b = &_b;
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&u, 0, DT_REAL, _state, ae_true);
|
|
|
|
if( x->cnt<n )
|
|
{
|
|
ae_vector_set_length(x, n, _state);
|
|
}
|
|
beta = a->ptr.p_double[0];
|
|
alpha = c->ptr.p_double[n-1];
|
|
gamma = -b->ptr.p_double[0];
|
|
b->ptr.p_double[0] = 2*b->ptr.p_double[0];
|
|
b->ptr.p_double[n-1] = b->ptr.p_double[n-1]-alpha*beta/gamma;
|
|
ae_vector_set_length(&u, n, _state);
|
|
for(k=0; k<=n-1; k++)
|
|
{
|
|
u.ptr.p_double[k] = 0;
|
|
}
|
|
u.ptr.p_double[0] = gamma;
|
|
u.ptr.p_double[n-1] = alpha;
|
|
spline1d_solvetridiagonal(a, b, c, d, n, &y, _state);
|
|
spline1d_solvetridiagonal(a, b, c, &u, n, &z, _state);
|
|
for(k=0; k<=n-1; k++)
|
|
{
|
|
x->ptr.p_double[k] = y.ptr.p_double[k]-(y.ptr.p_double[0]+beta/gamma*y.ptr.p_double[n-1])/(1+z.ptr.p_double[0]+beta/gamma*z.ptr.p_double[n-1])*z.ptr.p_double[k];
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine. Three-point differentiation
|
|
*************************************************************************/
|
|
static double spline1d_diffthreepoint(double t,
|
|
double x0,
|
|
double f0,
|
|
double x1,
|
|
double f1,
|
|
double x2,
|
|
double f2,
|
|
ae_state *_state)
|
|
{
|
|
double a;
|
|
double b;
|
|
double result;
|
|
|
|
|
|
t = t-x0;
|
|
x1 = x1-x0;
|
|
x2 = x2-x0;
|
|
a = (f2-f0-x2/x1*(f1-f0))/(ae_sqr(x2, _state)-x1*x2);
|
|
b = (f1-f0-a*ae_sqr(x1, _state))/x1;
|
|
result = 2*a*t+b;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Procedure for calculating value of a function is providet in the form of
|
|
Hermite polinom
|
|
|
|
INPUT PARAMETERS:
|
|
P0 - value of a function at 0
|
|
M0 - value of a derivative at 0
|
|
P1 - value of a function at 1
|
|
M1 - value of a derivative at 1
|
|
T - point inside [0;1]
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - value of a function at T
|
|
B0 - value of a derivative function at T
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.09.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline1d_hermitecalc(double p0,
|
|
double m0,
|
|
double p1,
|
|
double m1,
|
|
double t,
|
|
double* s,
|
|
double* ds,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*s = 0;
|
|
*ds = 0;
|
|
|
|
*s = p0*(1+2*t)*(1-t)*(1-t)+m0*t*(1-t)*(1-t)+p1*(3-2*t)*t*t+m1*t*t*(t-1);
|
|
*ds = -p0*6*t*(1-t)+m0*(1-t)*(1-3*t)+p1*6*t*(1-t)+m1*t*(3*t-2);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Function for mapping from [A0;B0] to [A1;B1]
|
|
|
|
INPUT PARAMETERS:
|
|
A0 - left border [A0;B0]
|
|
B0 - right border [A0;B0]
|
|
A1 - left border [A1;B1]
|
|
B1 - right border [A1;B1]
|
|
T - value inside [A0;B0]
|
|
|
|
RESTRICTIONS OF PARAMETERS:
|
|
|
|
We assume, that B0>A0 and B1>A1. But we chech, that T is inside [A0;B0],
|
|
and if T<A0 then T become A1, if T>B0 then T - B1.
|
|
|
|
INPUT PARAMETERS:
|
|
A0 - left border for segment [A0;B0] from 'T' is converted to [A1;B1]
|
|
B0 - right border for segment [A0;B0] from 'T' is converted to [A1;B1]
|
|
A1 - left border for segment [A1;B1] to 'T' is converted from [A0;B0]
|
|
B1 - right border for segment [A1;B1] to 'T' is converted from [A0;B0]
|
|
T - the parameter is mapped from [A0;B0] to [A1;B1]
|
|
|
|
Result:
|
|
is converted value for 'T' from [A0;B0] to [A1;B1]
|
|
|
|
REMARK:
|
|
|
|
The function dont check value A0,B0 and A1,B1!
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.09.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static double spline1d_rescaleval(double a0,
|
|
double b0,
|
|
double a1,
|
|
double b1,
|
|
double t,
|
|
ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
|
|
/*
|
|
*return left border
|
|
*/
|
|
if( ae_fp_less_eq(t,a0) )
|
|
{
|
|
result = a1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
*return right border
|
|
*/
|
|
if( ae_fp_greater_eq(t,b0) )
|
|
{
|
|
result = b1;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
*return value between left and right borders
|
|
*/
|
|
result = (b1-a1)*(t-a0)/(b0-a0)+a1;
|
|
return result;
|
|
}
|
|
|
|
|
|
ae_bool _spline1dinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline1dinterpolant *p = (spline1dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _spline1dinterpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline1dinterpolant *dst = (spline1dinterpolant*)_dst;
|
|
spline1dinterpolant *src = (spline1dinterpolant*)_src;
|
|
dst->periodic = src->periodic;
|
|
dst->n = src->n;
|
|
dst->k = src->k;
|
|
dst->continuity = src->continuity;
|
|
if( !ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _spline1dinterpolant_clear(void* _p)
|
|
{
|
|
spline1dinterpolant *p = (spline1dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->x);
|
|
ae_vector_clear(&p->c);
|
|
}
|
|
|
|
|
|
void _spline1dinterpolant_destroy(void* _p)
|
|
{
|
|
spline1dinterpolant *p = (spline1dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->x);
|
|
ae_vector_destroy(&p->c);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
Fitting by polynomials in barycentric form. This function provides simple
|
|
unterface for unconstrained unweighted fitting. See PolynomialFitWC() if
|
|
you need constrained fitting.
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO:
|
|
PolynomialFitWC()
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0
|
|
* if given, only leading N elements of X/Y are used
|
|
* if not given, automatically determined from sizes of X/Y
|
|
M - number of basis functions (= polynomial_degree + 1), M>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
P - interpolant in barycentric form.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialfit(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
barycentricinterpolant* p,
|
|
polynomialfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector w;
|
|
ae_vector xc;
|
|
ae_vector yc;
|
|
ae_vector dc;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
_barycentricinterpolant_clear(p);
|
|
_polynomialfitreport_clear(rep);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_assert(n>0, "PolynomialFit: N<=0!", _state);
|
|
ae_assert(m>0, "PolynomialFit: M<=0!", _state);
|
|
ae_assert(x->cnt>=n, "PolynomialFit: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "PolynomialFit: Length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "PolynomialFit: X contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "PolynomialFit: Y contains infinite or NaN values!", _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = 1;
|
|
}
|
|
polynomialfitwc(x, y, &w, n, &xc, &yc, &dc, 0, m, info, p, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by polynomials in barycentric form, with constraints on
|
|
function values or first derivatives.
|
|
|
|
Small regularizing term is used when solving constrained tasks (to improve
|
|
stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO:
|
|
PolynomialFit()
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points, N>0.
|
|
* if given, only leading N elements of X/Y/W are used
|
|
* if not given, automatically determined from sizes of X/Y/W
|
|
XC - points where polynomial values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that P(XC[i])=YC[i]
|
|
* DC[i]=1 means that P'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints, 0<=K<M.
|
|
K=0 means no constraints (XC/YC/DC are not used in such cases)
|
|
M - number of basis functions (= polynomial_degree + 1), M>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
P - interpolant in barycentric form.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* even simple constraints can be inconsistent, see Wikipedia article on
|
|
this subject: http://en.wikipedia.org/wiki/Birkhoff_interpolation
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints is NOT GUARANTEED.
|
|
* in the one special cases, however, we can guarantee consistency. This
|
|
case is: M>1 and constraints on the function values (NOT DERIVATIVES)
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY when you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void polynomialfitwc(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
barycentricinterpolant* p,
|
|
polynomialfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _w;
|
|
ae_vector _xc;
|
|
ae_vector _yc;
|
|
double xa;
|
|
double xb;
|
|
double sa;
|
|
double sb;
|
|
ae_vector xoriginal;
|
|
ae_vector yoriginal;
|
|
ae_vector y2;
|
|
ae_vector w2;
|
|
ae_vector tmp;
|
|
ae_vector tmp2;
|
|
ae_vector bx;
|
|
ae_vector by;
|
|
ae_vector bw;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double u;
|
|
double v;
|
|
double s;
|
|
ae_int_t relcnt;
|
|
lsfitreport lrep;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_w, w, _state, ae_true);
|
|
w = &_w;
|
|
ae_vector_init_copy(&_xc, xc, _state, ae_true);
|
|
xc = &_xc;
|
|
ae_vector_init_copy(&_yc, yc, _state, ae_true);
|
|
yc = &_yc;
|
|
*info = 0;
|
|
_barycentricinterpolant_clear(p);
|
|
_polynomialfitreport_clear(rep);
|
|
ae_vector_init(&xoriginal, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yoriginal, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&by, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bw, 0, DT_REAL, _state, ae_true);
|
|
_lsfitreport_init(&lrep, _state, ae_true);
|
|
|
|
ae_assert(n>0, "PolynomialFitWC: N<=0!", _state);
|
|
ae_assert(m>0, "PolynomialFitWC: M<=0!", _state);
|
|
ae_assert(k>=0, "PolynomialFitWC: K<0!", _state);
|
|
ae_assert(k<m, "PolynomialFitWC: K>=M!", _state);
|
|
ae_assert(x->cnt>=n, "PolynomialFitWC: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "PolynomialFitWC: Length(Y)<N!", _state);
|
|
ae_assert(w->cnt>=n, "PolynomialFitWC: Length(W)<N!", _state);
|
|
ae_assert(xc->cnt>=k, "PolynomialFitWC: Length(XC)<K!", _state);
|
|
ae_assert(yc->cnt>=k, "PolynomialFitWC: Length(YC)<K!", _state);
|
|
ae_assert(dc->cnt>=k, "PolynomialFitWC: Length(DC)<K!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "PolynomialFitWC: X contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "PolynomialFitWC: Y contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "PolynomialFitWC: X contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(xc, k, _state), "PolynomialFitWC: XC contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(yc, k, _state), "PolynomialFitWC: YC contains infinite or NaN values!", _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(dc->ptr.p_int[i]==0||dc->ptr.p_int[i]==1, "PolynomialFitWC: one of DC[] is not 0 or 1!", _state);
|
|
}
|
|
|
|
/*
|
|
* Scale X, Y, XC, YC.
|
|
* Solve scaled problem using internal Chebyshev fitting function.
|
|
*/
|
|
lsfitscalexy(x, y, w, n, xc, yc, dc, k, &xa, &xb, &sa, &sb, &xoriginal, &yoriginal, _state);
|
|
lsfit_internalchebyshevfit(x, y, w, n, xc, yc, dc, k, m, info, &tmp, &lrep, _state);
|
|
if( *info<0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Generate barycentric model and scale it
|
|
* * BX, BY store barycentric model nodes
|
|
* * FMatrix is reused (remember - it is at least MxM, what we need)
|
|
*
|
|
* Model intialization is done in O(M^2). In principle, it can be
|
|
* done in O(M*log(M)), but before it we solved task with O(N*M^2)
|
|
* complexity, so it is only a small amount of total time spent.
|
|
*/
|
|
ae_vector_set_length(&bx, m, _state);
|
|
ae_vector_set_length(&by, m, _state);
|
|
ae_vector_set_length(&bw, m, _state);
|
|
ae_vector_set_length(&tmp2, m, _state);
|
|
s = 1;
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
if( m!=1 )
|
|
{
|
|
u = ae_cos(ae_pi*i/(m-1), _state);
|
|
}
|
|
else
|
|
{
|
|
u = 0;
|
|
}
|
|
v = 0;
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
if( j==0 )
|
|
{
|
|
tmp2.ptr.p_double[j] = 1;
|
|
}
|
|
else
|
|
{
|
|
if( j==1 )
|
|
{
|
|
tmp2.ptr.p_double[j] = u;
|
|
}
|
|
else
|
|
{
|
|
tmp2.ptr.p_double[j] = 2*u*tmp2.ptr.p_double[j-1]-tmp2.ptr.p_double[j-2];
|
|
}
|
|
}
|
|
v = v+tmp.ptr.p_double[j]*tmp2.ptr.p_double[j];
|
|
}
|
|
bx.ptr.p_double[i] = u;
|
|
by.ptr.p_double[i] = v;
|
|
bw.ptr.p_double[i] = s;
|
|
if( i==0||i==m-1 )
|
|
{
|
|
bw.ptr.p_double[i] = 0.5*bw.ptr.p_double[i];
|
|
}
|
|
s = -s;
|
|
}
|
|
barycentricbuildxyw(&bx, &by, &bw, m, p, _state);
|
|
barycentriclintransx(p, 2/(xb-xa), -(xa+xb)/(xb-xa), _state);
|
|
barycentriclintransy(p, sb-sa, sa, _state);
|
|
|
|
/*
|
|
* Scale absolute errors obtained from LSFitLinearW.
|
|
* Relative error should be calculated separately
|
|
* (because of shifting/scaling of the task)
|
|
*/
|
|
rep->taskrcond = lrep.taskrcond;
|
|
rep->rmserror = lrep.rmserror*(sb-sa);
|
|
rep->avgerror = lrep.avgerror*(sb-sa);
|
|
rep->maxerror = lrep.maxerror*(sb-sa);
|
|
rep->avgrelerror = 0;
|
|
relcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(yoriginal.ptr.p_double[i],0) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror+ae_fabs(barycentriccalc(p, xoriginal.ptr.p_double[i], _state)-yoriginal.ptr.p_double[i], _state)/ae_fabs(yoriginal.ptr.p_double[i], _state);
|
|
relcnt = relcnt+1;
|
|
}
|
|
}
|
|
if( relcnt!=0 )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror/relcnt;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weghted rational least squares fitting using Floater-Hormann rational
|
|
functions with optimal D chosen from [0,9], with constraints and
|
|
individual weights.
|
|
|
|
Equidistant grid with M node on [min(x),max(x)] is used to build basis
|
|
functions. Different values of D are tried, optimal D (least WEIGHTED root
|
|
mean square error) is chosen. Task is linear, so linear least squares
|
|
solver is used. Complexity of this computational scheme is O(N*M^2)
|
|
(mostly dominated by the least squares solver).
|
|
|
|
SEE ALSO
|
|
* BarycentricFitFloaterHormann(), "lightweight" fitting without invididual
|
|
weights and constraints.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points, N>0.
|
|
XC - points where function values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints, 0<=K<M.
|
|
K=0 means no constraints (XC/YC/DC are not used in such cases)
|
|
M - number of basis functions ( = number_of_nodes), M>=2.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
-1 means another errors in parameters passed
|
|
(N<=0, for example)
|
|
B - barycentric interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* DBest best value of the D parameter
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroutine doesn't calculate task's condition number for K<>0.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained barycentric interpolants:
|
|
* excessive constraints can be inconsistent. Floater-Hormann basis
|
|
functions aren't as flexible as splines (although they are very smooth).
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints IS NOT GUARANTEED.
|
|
* in the several special cases, however, we CAN guarantee consistency.
|
|
* one of this cases is constraints on the function VALUES at the interval
|
|
boundaries. Note that consustency of the constraints on the function
|
|
DERIVATIVES is NOT guaranteed (you can use in such cases cubic splines
|
|
which are more flexible).
|
|
* another special case is ONE constraint on the function value (OR, but
|
|
not AND, derivative) anywhere in the interval
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY WHEN you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricfitfloaterhormannwc(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
barycentricinterpolant* b,
|
|
barycentricfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t d;
|
|
ae_int_t i;
|
|
double wrmscur;
|
|
double wrmsbest;
|
|
barycentricinterpolant locb;
|
|
barycentricfitreport locrep;
|
|
ae_int_t locinfo;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
_barycentricinterpolant_clear(b);
|
|
_barycentricfitreport_clear(rep);
|
|
_barycentricinterpolant_init(&locb, _state, ae_true);
|
|
_barycentricfitreport_init(&locrep, _state, ae_true);
|
|
|
|
ae_assert(n>0, "BarycentricFitFloaterHormannWC: N<=0!", _state);
|
|
ae_assert(m>0, "BarycentricFitFloaterHormannWC: M<=0!", _state);
|
|
ae_assert(k>=0, "BarycentricFitFloaterHormannWC: K<0!", _state);
|
|
ae_assert(k<m, "BarycentricFitFloaterHormannWC: K>=M!", _state);
|
|
ae_assert(x->cnt>=n, "BarycentricFitFloaterHormannWC: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "BarycentricFitFloaterHormannWC: Length(Y)<N!", _state);
|
|
ae_assert(w->cnt>=n, "BarycentricFitFloaterHormannWC: Length(W)<N!", _state);
|
|
ae_assert(xc->cnt>=k, "BarycentricFitFloaterHormannWC: Length(XC)<K!", _state);
|
|
ae_assert(yc->cnt>=k, "BarycentricFitFloaterHormannWC: Length(YC)<K!", _state);
|
|
ae_assert(dc->cnt>=k, "BarycentricFitFloaterHormannWC: Length(DC)<K!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "BarycentricFitFloaterHormannWC: X contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "BarycentricFitFloaterHormannWC: Y contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "BarycentricFitFloaterHormannWC: X contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(xc, k, _state), "BarycentricFitFloaterHormannWC: XC contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(yc, k, _state), "BarycentricFitFloaterHormannWC: YC contains infinite or NaN values!", _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(dc->ptr.p_int[i]==0||dc->ptr.p_int[i]==1, "BarycentricFitFloaterHormannWC: one of DC[] is not 0 or 1!", _state);
|
|
}
|
|
|
|
/*
|
|
* Find optimal D
|
|
*
|
|
* Info is -3 by default (degenerate constraints).
|
|
* If LocInfo will always be equal to -3, Info will remain equal to -3.
|
|
* If at least once LocInfo will be -4, Info will be -4.
|
|
*/
|
|
wrmsbest = ae_maxrealnumber;
|
|
rep->dbest = -1;
|
|
*info = -3;
|
|
for(d=0; d<=ae_minint(9, n-1, _state); d++)
|
|
{
|
|
lsfit_barycentricfitwcfixedd(x, y, w, n, xc, yc, dc, k, m, d, &locinfo, &locb, &locrep, _state);
|
|
ae_assert((locinfo==-4||locinfo==-3)||locinfo>0, "BarycentricFitFloaterHormannWC: unexpected result from BarycentricFitWCFixedD!", _state);
|
|
if( locinfo>0 )
|
|
{
|
|
|
|
/*
|
|
* Calculate weghted RMS
|
|
*/
|
|
wrmscur = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
wrmscur = wrmscur+ae_sqr(w->ptr.p_double[i]*(y->ptr.p_double[i]-barycentriccalc(&locb, x->ptr.p_double[i], _state)), _state);
|
|
}
|
|
wrmscur = ae_sqrt(wrmscur/n, _state);
|
|
if( ae_fp_less(wrmscur,wrmsbest)||rep->dbest<0 )
|
|
{
|
|
barycentriccopy(&locb, b, _state);
|
|
rep->dbest = d;
|
|
*info = 1;
|
|
rep->rmserror = locrep.rmserror;
|
|
rep->avgerror = locrep.avgerror;
|
|
rep->avgrelerror = locrep.avgrelerror;
|
|
rep->maxerror = locrep.maxerror;
|
|
rep->taskrcond = locrep.taskrcond;
|
|
wrmsbest = wrmscur;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( locinfo!=-3&&*info<0 )
|
|
{
|
|
*info = locinfo;
|
|
}
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Rational least squares fitting using Floater-Hormann rational functions
|
|
with optimal D chosen from [0,9].
|
|
|
|
Equidistant grid with M node on [min(x),max(x)] is used to build basis
|
|
functions. Different values of D are tried, optimal D (least root mean
|
|
square error) is chosen. Task is linear, so linear least squares solver
|
|
is used. Complexity of this computational scheme is O(N*M^2) (mostly
|
|
dominated by the least squares solver).
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0.
|
|
M - number of basis functions ( = number_of_nodes), M>=2.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
B - barycentric interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* DBest best value of the D parameter
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void barycentricfitfloaterhormann(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
barycentricinterpolant* b,
|
|
barycentricfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector w;
|
|
ae_vector xc;
|
|
ae_vector yc;
|
|
ae_vector dc;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
_barycentricinterpolant_clear(b);
|
|
_barycentricfitreport_clear(rep);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_assert(n>0, "BarycentricFitFloaterHormann: N<=0!", _state);
|
|
ae_assert(m>0, "BarycentricFitFloaterHormann: M<=0!", _state);
|
|
ae_assert(x->cnt>=n, "BarycentricFitFloaterHormann: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "BarycentricFitFloaterHormann: Length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "BarycentricFitFloaterHormann: X contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "BarycentricFitFloaterHormann: Y contains infinite or NaN values!", _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = 1;
|
|
}
|
|
barycentricfitfloaterhormannwc(x, y, &w, n, &xc, &yc, &dc, 0, m, info, b, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Rational least squares fitting using Floater-Hormann rational functions
|
|
with optimal D chosen from [0,9].
|
|
|
|
Equidistant grid with M node on [min(x),max(x)] is used to build basis
|
|
functions. Different values of D are tried, optimal D (least root mean
|
|
square error) is chosen. Task is linear, so linear least squares solver
|
|
is used. Complexity of this computational scheme is O(N*M^2) (mostly
|
|
dominated by the least squares solver).
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points, N>0.
|
|
M - number of basis functions ( = number_of_nodes), M>=2.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
B - barycentric interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* DBest best value of the D parameter
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitpenalized(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
double rho,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector w;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
*info = 0;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "Spline1DFitPenalized: N<1!", _state);
|
|
ae_assert(m>=4, "Spline1DFitPenalized: M<4!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DFitPenalized: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DFitPenalized: Length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DFitPenalized: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DFitPenalized: Y contains infinite or NAN values!", _state);
|
|
ae_assert(ae_isfinite(rho, _state), "Spline1DFitPenalized: Rho is infinite!", _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = 1;
|
|
}
|
|
spline1dfitpenalizedw(x, y, &w, n, m, rho, info, s, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by penalized cubic spline.
|
|
|
|
Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are cubic splines with natural boundary
|
|
conditions. Problem is regularized by adding non-linearity penalty to the
|
|
usual least squares penalty function:
|
|
|
|
S(x) = arg min { LS + P }, where
|
|
LS = SUM { w[i]^2*(y[i] - S(x[i]))^2 } - least squares penalty
|
|
P = C*10^rho*integral{ S''(x)^2*dx } - non-linearity penalty
|
|
rho - tunable constant given by user
|
|
C - automatically determined scale parameter,
|
|
makes penalty invariant with respect to scaling of X, Y, W.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
problem.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
M - number of basis functions ( = number_of_nodes), M>=4.
|
|
Rho - regularization constant passed by user. It penalizes
|
|
nonlinearity in the regression spline. It is logarithmically
|
|
scaled, i.e. actual value of regularization constant is
|
|
calculated as 10^Rho. It is automatically scaled so that:
|
|
* Rho=2.0 corresponds to moderate amount of nonlinearity
|
|
* generally, it should be somewhere in the [-8.0,+8.0]
|
|
If you do not want to penalize nonlineary,
|
|
pass small Rho. Values as low as -15 should work.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD or
|
|
Cholesky decomposition; problem may be
|
|
too ill-conditioned (very rare)
|
|
S - spline interpolant.
|
|
Rep - Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
NOTE 1: additional nodes are added to the spline outside of the fitting
|
|
interval to force linearity when x<min(x,xc) or x>max(x,xc). It is done
|
|
for consistency - we penalize non-linearity at [min(x,xc),max(x,xc)], so
|
|
it is natural to force linearity outside of this interval.
|
|
|
|
NOTE 2: function automatically sorts points, so caller may pass unsorted
|
|
array.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.10.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitpenalizedw(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
double rho,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _w;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t b;
|
|
double v;
|
|
double relcnt;
|
|
double xa;
|
|
double xb;
|
|
double sa;
|
|
double sb;
|
|
ae_vector xoriginal;
|
|
ae_vector yoriginal;
|
|
double pdecay;
|
|
double tdecay;
|
|
ae_matrix fmatrix;
|
|
ae_vector fcolumn;
|
|
ae_vector y2;
|
|
ae_vector w2;
|
|
ae_vector xc;
|
|
ae_vector yc;
|
|
ae_vector dc;
|
|
double fdmax;
|
|
double admax;
|
|
ae_matrix amatrix;
|
|
ae_matrix d2matrix;
|
|
double fa;
|
|
double ga;
|
|
double fb;
|
|
double gb;
|
|
double lambdav;
|
|
ae_vector bx;
|
|
ae_vector by;
|
|
ae_vector bd1;
|
|
ae_vector bd2;
|
|
ae_vector tx;
|
|
ae_vector ty;
|
|
ae_vector td;
|
|
spline1dinterpolant bs;
|
|
ae_matrix nmatrix;
|
|
ae_vector rightpart;
|
|
fblslincgstate cgstate;
|
|
ae_vector c;
|
|
ae_vector tmp0;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_w, w, _state, ae_true);
|
|
w = &_w;
|
|
*info = 0;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
ae_vector_init(&xoriginal, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yoriginal, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&fmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&fcolumn, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
|
|
ae_matrix_init(&amatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&d2matrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&by, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bd1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bd2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ty, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&td, 0, DT_REAL, _state, ae_true);
|
|
_spline1dinterpolant_init(&bs, _state, ae_true);
|
|
ae_matrix_init(&nmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&rightpart, 0, DT_REAL, _state, ae_true);
|
|
_fblslincgstate_init(&cgstate, _state, ae_true);
|
|
ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "Spline1DFitPenalizedW: N<1!", _state);
|
|
ae_assert(m>=4, "Spline1DFitPenalizedW: M<4!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DFitPenalizedW: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DFitPenalizedW: Length(Y)<N!", _state);
|
|
ae_assert(w->cnt>=n, "Spline1DFitPenalizedW: Length(W)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DFitPenalizedW: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DFitPenalizedW: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "Spline1DFitPenalizedW: Y contains infinite or NAN values!", _state);
|
|
ae_assert(ae_isfinite(rho, _state), "Spline1DFitPenalizedW: Rho is infinite!", _state);
|
|
|
|
/*
|
|
* Prepare LambdaV
|
|
*/
|
|
v = -ae_log(ae_machineepsilon, _state)/ae_log(10, _state);
|
|
if( ae_fp_less(rho,-v) )
|
|
{
|
|
rho = -v;
|
|
}
|
|
if( ae_fp_greater(rho,v) )
|
|
{
|
|
rho = v;
|
|
}
|
|
lambdav = ae_pow(10, rho, _state);
|
|
|
|
/*
|
|
* Sort X, Y, W
|
|
*/
|
|
heapsortdpoints(x, y, w, n, _state);
|
|
|
|
/*
|
|
* Scale X, Y, XC, YC
|
|
*/
|
|
lsfitscalexy(x, y, w, n, &xc, &yc, &dc, 0, &xa, &xb, &sa, &sb, &xoriginal, &yoriginal, _state);
|
|
|
|
/*
|
|
* Allocate space
|
|
*/
|
|
ae_matrix_set_length(&fmatrix, n, m, _state);
|
|
ae_matrix_set_length(&amatrix, m, m, _state);
|
|
ae_matrix_set_length(&d2matrix, m, m, _state);
|
|
ae_vector_set_length(&bx, m, _state);
|
|
ae_vector_set_length(&by, m, _state);
|
|
ae_vector_set_length(&fcolumn, n, _state);
|
|
ae_matrix_set_length(&nmatrix, m, m, _state);
|
|
ae_vector_set_length(&rightpart, m, _state);
|
|
ae_vector_set_length(&tmp0, ae_maxint(m, n, _state), _state);
|
|
ae_vector_set_length(&c, m, _state);
|
|
|
|
/*
|
|
* Fill:
|
|
* * FMatrix by values of basis functions
|
|
* * TmpAMatrix by second derivatives of I-th function at J-th point
|
|
* * CMatrix by constraints
|
|
*/
|
|
fdmax = 0;
|
|
for(b=0; b<=m-1; b++)
|
|
{
|
|
|
|
/*
|
|
* Prepare I-th basis function
|
|
*/
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
bx.ptr.p_double[j] = (double)(2*j)/(double)(m-1)-1;
|
|
by.ptr.p_double[j] = 0;
|
|
}
|
|
by.ptr.p_double[b] = 1;
|
|
spline1dgriddiff2cubic(&bx, &by, m, 2, 0.0, 2, 0.0, &bd1, &bd2, _state);
|
|
spline1dbuildcubic(&bx, &by, m, 2, 0.0, 2, 0.0, &bs, _state);
|
|
|
|
/*
|
|
* Calculate B-th column of FMatrix
|
|
* Update FDMax (maximum column norm)
|
|
*/
|
|
spline1dconvcubic(&bx, &by, m, 2, 0.0, 2, 0.0, x, n, &fcolumn, _state);
|
|
ae_v_move(&fmatrix.ptr.pp_double[0][b], fmatrix.stride, &fcolumn.ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
v = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = v+ae_sqr(w->ptr.p_double[i]*fcolumn.ptr.p_double[i], _state);
|
|
}
|
|
fdmax = ae_maxreal(fdmax, v, _state);
|
|
|
|
/*
|
|
* Fill temporary with second derivatives of basis function
|
|
*/
|
|
ae_v_move(&d2matrix.ptr.pp_double[b][0], 1, &bd2.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
}
|
|
|
|
/*
|
|
* * calculate penalty matrix A
|
|
* * calculate max of diagonal elements of A
|
|
* * calculate PDecay - coefficient before penalty matrix
|
|
*/
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=i; j<=m-1; j++)
|
|
{
|
|
|
|
/*
|
|
* calculate integral(B_i''*B_j'') where B_i and B_j are
|
|
* i-th and j-th basis splines.
|
|
* B_i and B_j are piecewise linear functions.
|
|
*/
|
|
v = 0;
|
|
for(b=0; b<=m-2; b++)
|
|
{
|
|
fa = d2matrix.ptr.pp_double[i][b];
|
|
fb = d2matrix.ptr.pp_double[i][b+1];
|
|
ga = d2matrix.ptr.pp_double[j][b];
|
|
gb = d2matrix.ptr.pp_double[j][b+1];
|
|
v = v+(bx.ptr.p_double[b+1]-bx.ptr.p_double[b])*(fa*ga+(fa*(gb-ga)+ga*(fb-fa))/2+(fb-fa)*(gb-ga)/3);
|
|
}
|
|
amatrix.ptr.pp_double[i][j] = v;
|
|
amatrix.ptr.pp_double[j][i] = v;
|
|
}
|
|
}
|
|
admax = 0;
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
admax = ae_maxreal(admax, ae_fabs(amatrix.ptr.pp_double[i][i], _state), _state);
|
|
}
|
|
pdecay = lambdav*fdmax/admax;
|
|
|
|
/*
|
|
* Calculate TDecay for Tikhonov regularization
|
|
*/
|
|
tdecay = fdmax*(1+pdecay)*10*ae_machineepsilon;
|
|
|
|
/*
|
|
* Prepare system
|
|
*
|
|
* NOTE: FMatrix is spoiled during this process
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = w->ptr.p_double[i];
|
|
ae_v_muld(&fmatrix.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
|
|
}
|
|
rmatrixgemm(m, m, n, 1.0, &fmatrix, 0, 0, 1, &fmatrix, 0, 0, 0, 0.0, &nmatrix, 0, 0, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
nmatrix.ptr.pp_double[i][j] = nmatrix.ptr.pp_double[i][j]+pdecay*amatrix.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
nmatrix.ptr.pp_double[i][i] = nmatrix.ptr.pp_double[i][i]+tdecay;
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
rightpart.ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = y->ptr.p_double[i]*w->ptr.p_double[i];
|
|
ae_v_addd(&rightpart.ptr.p_double[0], 1, &fmatrix.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
|
|
}
|
|
|
|
/*
|
|
* Solve system
|
|
*/
|
|
if( !spdmatrixcholesky(&nmatrix, m, ae_true, _state) )
|
|
{
|
|
*info = -4;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
fblscholeskysolve(&nmatrix, 1.0, m, ae_true, &rightpart, &tmp0, _state);
|
|
ae_v_move(&c.ptr.p_double[0], 1, &rightpart.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
|
|
/*
|
|
* add nodes to force linearity outside of the fitting interval
|
|
*/
|
|
spline1dgriddiffcubic(&bx, &c, m, 2, 0.0, 2, 0.0, &bd1, _state);
|
|
ae_vector_set_length(&tx, m+2, _state);
|
|
ae_vector_set_length(&ty, m+2, _state);
|
|
ae_vector_set_length(&td, m+2, _state);
|
|
ae_v_move(&tx.ptr.p_double[1], 1, &bx.ptr.p_double[0], 1, ae_v_len(1,m));
|
|
ae_v_move(&ty.ptr.p_double[1], 1, &rightpart.ptr.p_double[0], 1, ae_v_len(1,m));
|
|
ae_v_move(&td.ptr.p_double[1], 1, &bd1.ptr.p_double[0], 1, ae_v_len(1,m));
|
|
tx.ptr.p_double[0] = tx.ptr.p_double[1]-(tx.ptr.p_double[2]-tx.ptr.p_double[1]);
|
|
ty.ptr.p_double[0] = ty.ptr.p_double[1]-td.ptr.p_double[1]*(tx.ptr.p_double[2]-tx.ptr.p_double[1]);
|
|
td.ptr.p_double[0] = td.ptr.p_double[1];
|
|
tx.ptr.p_double[m+1] = tx.ptr.p_double[m]+(tx.ptr.p_double[m]-tx.ptr.p_double[m-1]);
|
|
ty.ptr.p_double[m+1] = ty.ptr.p_double[m]+td.ptr.p_double[m]*(tx.ptr.p_double[m]-tx.ptr.p_double[m-1]);
|
|
td.ptr.p_double[m+1] = td.ptr.p_double[m];
|
|
spline1dbuildhermite(&tx, &ty, &td, m+2, s, _state);
|
|
spline1dlintransx(s, 2/(xb-xa), -(xa+xb)/(xb-xa), _state);
|
|
spline1dlintransy(s, sb-sa, sa, _state);
|
|
*info = 1;
|
|
|
|
/*
|
|
* Fill report
|
|
*/
|
|
rep->rmserror = 0;
|
|
rep->avgerror = 0;
|
|
rep->avgrelerror = 0;
|
|
rep->maxerror = 0;
|
|
relcnt = 0;
|
|
spline1dconvcubic(&bx, &rightpart, m, 2, 0.0, 2, 0.0, x, n, &fcolumn, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = (sb-sa)*fcolumn.ptr.p_double[i]+sa;
|
|
rep->rmserror = rep->rmserror+ae_sqr(v-yoriginal.ptr.p_double[i], _state);
|
|
rep->avgerror = rep->avgerror+ae_fabs(v-yoriginal.ptr.p_double[i], _state);
|
|
if( ae_fp_neq(yoriginal.ptr.p_double[i],0) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror+ae_fabs(v-yoriginal.ptr.p_double[i], _state)/ae_fabs(yoriginal.ptr.p_double[i], _state);
|
|
relcnt = relcnt+1;
|
|
}
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v-yoriginal.ptr.p_double[i], _state), _state);
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/n, _state);
|
|
rep->avgerror = rep->avgerror/n;
|
|
if( ae_fp_neq(relcnt,0) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror/relcnt;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by cubic spline, with constraints on function values or
|
|
derivatives.
|
|
|
|
Equidistant grid with M-2 nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are cubic splines with continuous second
|
|
derivatives and non-fixed first derivatives at interval ends. Small
|
|
regularizing term is used when solving constrained tasks (to improve
|
|
stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO
|
|
Spline1DFitHermiteWC() - fitting by Hermite splines (more flexible,
|
|
less smooth)
|
|
Spline1DFitCubic() - "lightweight" fitting by cubic splines,
|
|
without invididual weights and constraints
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
XC - points where spline values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints (optional):
|
|
* 0<=K<M.
|
|
* K=0 means no constraints (XC/YC/DC are not used)
|
|
* if given, only first K elements of XC/YC/DC are used
|
|
* if not given, automatically determined from XC/YC/DC
|
|
M - number of basis functions ( = number_of_nodes+2), M>=4.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearWC() subroutine.
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
S - spline interpolant.
|
|
Rep - report, same format as in LSFitLinearWC() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* excessive constraints can be inconsistent. Splines are piecewise cubic
|
|
functions, and it is easy to create an example, where large number of
|
|
constraints concentrated in small area will result in inconsistency.
|
|
Just because spline is not flexible enough to satisfy all of them. And
|
|
same constraints spread across the [min(x),max(x)] will be perfectly
|
|
consistent.
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints IS NOT GUARANTEED.
|
|
* in the several special cases, however, we CAN guarantee consistency.
|
|
* one of this cases is constraints on the function values AND/OR its
|
|
derivatives at the interval boundaries.
|
|
* another special case is ONE constraint on the function value (OR, but
|
|
not AND, derivative) anywhere in the interval
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY WHEN you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitcubicwc(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
*info = 0;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
|
|
ae_assert(n>=1, "Spline1DFitCubicWC: N<1!", _state);
|
|
ae_assert(m>=4, "Spline1DFitCubicWC: M<4!", _state);
|
|
ae_assert(k>=0, "Spline1DFitCubicWC: K<0!", _state);
|
|
ae_assert(k<m, "Spline1DFitCubicWC: K>=M!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DFitCubicWC: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DFitCubicWC: Length(Y)<N!", _state);
|
|
ae_assert(w->cnt>=n, "Spline1DFitCubicWC: Length(W)<N!", _state);
|
|
ae_assert(xc->cnt>=k, "Spline1DFitCubicWC: Length(XC)<K!", _state);
|
|
ae_assert(yc->cnt>=k, "Spline1DFitCubicWC: Length(YC)<K!", _state);
|
|
ae_assert(dc->cnt>=k, "Spline1DFitCubicWC: Length(DC)<K!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DFitCubicWC: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DFitCubicWC: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "Spline1DFitCubicWC: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(xc, k, _state), "Spline1DFitCubicWC: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(yc, k, _state), "Spline1DFitCubicWC: Y contains infinite or NAN values!", _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(dc->ptr.p_int[i]==0||dc->ptr.p_int[i]==1, "Spline1DFitCubicWC: DC[i] is neither 0 or 1!", _state);
|
|
}
|
|
lsfit_spline1dfitinternal(0, x, y, w, n, xc, yc, dc, k, m, info, s, rep, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted fitting by Hermite spline, with constraints on function values
|
|
or first derivatives.
|
|
|
|
Equidistant grid with M nodes on [min(x,xc),max(x,xc)] is used to build
|
|
basis functions. Basis functions are Hermite splines. Small regularizing
|
|
term is used when solving constrained tasks (to improve stability).
|
|
|
|
Task is linear, so linear least squares solver is used. Complexity of this
|
|
computational scheme is O(N*M^2), mostly dominated by least squares solver
|
|
|
|
SEE ALSO
|
|
Spline1DFitCubicWC() - fitting by Cubic splines (less flexible,
|
|
more smooth)
|
|
Spline1DFitHermite() - "lightweight" Hermite fitting, without
|
|
invididual weights and constraints
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
Each summand in square sum of approximation deviations from
|
|
given values is multiplied by the square of corresponding
|
|
weight. Fill it by 1's if you don't want to solve weighted
|
|
task.
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y/W are processed
|
|
* if not given, automatically determined from X/Y/W sizes
|
|
XC - points where spline values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that S(XC[i])=YC[i]
|
|
* DC[i]=1 means that S'(XC[i])=YC[i]
|
|
SEE BELOW FOR IMPORTANT INFORMATION ON CONSTRAINTS
|
|
K - number of constraints (optional):
|
|
* 0<=K<M.
|
|
* K=0 means no constraints (XC/YC/DC are not used)
|
|
* if given, only first K elements of XC/YC/DC are used
|
|
* if not given, automatically determined from XC/YC/DC
|
|
M - number of basis functions (= 2 * number of nodes),
|
|
M>=4,
|
|
M IS EVEN!
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
-2 means odd M was passed (which is not supported)
|
|
-1 means another errors in parameters passed
|
|
(N<=0, for example)
|
|
S - spline interpolant.
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
IMPORTANT:
|
|
this subroitine supports only even M's
|
|
|
|
|
|
ORDER OF POINTS
|
|
|
|
Subroutine automatically sorts points, so caller may pass unsorted array.
|
|
|
|
SETTING CONSTRAINTS - DANGERS AND OPPORTUNITIES:
|
|
|
|
Setting constraints can lead to undesired results, like ill-conditioned
|
|
behavior, or inconsistency being detected. From the other side, it allows
|
|
us to improve quality of the fit. Here we summarize our experience with
|
|
constrained regression splines:
|
|
* excessive constraints can be inconsistent. Splines are piecewise cubic
|
|
functions, and it is easy to create an example, where large number of
|
|
constraints concentrated in small area will result in inconsistency.
|
|
Just because spline is not flexible enough to satisfy all of them. And
|
|
same constraints spread across the [min(x),max(x)] will be perfectly
|
|
consistent.
|
|
* the more evenly constraints are spread across [min(x),max(x)], the more
|
|
chances that they will be consistent
|
|
* the greater is M (given fixed constraints), the more chances that
|
|
constraints will be consistent
|
|
* in the general case, consistency of constraints is NOT GUARANTEED.
|
|
* in the several special cases, however, we can guarantee consistency.
|
|
* one of this cases is M>=4 and constraints on the function value
|
|
(AND/OR its derivative) at the interval boundaries.
|
|
* another special case is M>=4 and ONE constraint on the function value
|
|
(OR, BUT NOT AND, derivative) anywhere in [min(x),max(x)]
|
|
|
|
Our final recommendation is to use constraints WHEN AND ONLY when you
|
|
can't solve your task without them. Anything beyond special cases given
|
|
above is not guaranteed and may result in inconsistency.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfithermitewc(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
*info = 0;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
|
|
ae_assert(n>=1, "Spline1DFitHermiteWC: N<1!", _state);
|
|
ae_assert(m>=4, "Spline1DFitHermiteWC: M<4!", _state);
|
|
ae_assert(m%2==0, "Spline1DFitHermiteWC: M is odd!", _state);
|
|
ae_assert(k>=0, "Spline1DFitHermiteWC: K<0!", _state);
|
|
ae_assert(k<m, "Spline1DFitHermiteWC: K>=M!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DFitHermiteWC: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DFitHermiteWC: Length(Y)<N!", _state);
|
|
ae_assert(w->cnt>=n, "Spline1DFitHermiteWC: Length(W)<N!", _state);
|
|
ae_assert(xc->cnt>=k, "Spline1DFitHermiteWC: Length(XC)<K!", _state);
|
|
ae_assert(yc->cnt>=k, "Spline1DFitHermiteWC: Length(YC)<K!", _state);
|
|
ae_assert(dc->cnt>=k, "Spline1DFitHermiteWC: Length(DC)<K!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DFitHermiteWC: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DFitHermiteWC: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "Spline1DFitHermiteWC: Y contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(xc, k, _state), "Spline1DFitHermiteWC: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(yc, k, _state), "Spline1DFitHermiteWC: Y contains infinite or NAN values!", _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(dc->ptr.p_int[i]==0||dc->ptr.p_int[i]==1, "Spline1DFitHermiteWC: DC[i] is neither 0 or 1!", _state);
|
|
}
|
|
lsfit_spline1dfitinternal(1, x, y, w, n, xc, yc, dc, k, m, info, s, rep, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Least squares fitting by cubic spline.
|
|
|
|
This subroutine is "lightweight" alternative for more complex and feature-
|
|
rich Spline1DFitCubicWC(). See Spline1DFitCubicWC() for more information
|
|
about subroutine parameters (we don't duplicate it here because of length)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfitcubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector w;
|
|
ae_vector xc;
|
|
ae_vector yc;
|
|
ae_vector dc;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "Spline1DFitCubic: N<1!", _state);
|
|
ae_assert(m>=4, "Spline1DFitCubic: M<4!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DFitCubic: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DFitCubic: Length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DFitCubic: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DFitCubic: Y contains infinite or NAN values!", _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = 1;
|
|
}
|
|
spline1dfitcubicwc(x, y, &w, n, &xc, &yc, &dc, 0, m, info, s, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Least squares fitting by Hermite spline.
|
|
|
|
This subroutine is "lightweight" alternative for more complex and feature-
|
|
rich Spline1DFitHermiteWC(). See Spline1DFitHermiteWC() description for
|
|
more information about subroutine parameters (we don't duplicate it here
|
|
because of length).
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfithermite(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector w;
|
|
ae_vector xc;
|
|
ae_vector yc;
|
|
ae_vector dc;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dc, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "Spline1DFitHermite: N<1!", _state);
|
|
ae_assert(m>=4, "Spline1DFitHermite: M<4!", _state);
|
|
ae_assert(m%2==0, "Spline1DFitHermite: M is odd!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DFitHermite: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DFitHermite: Length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DFitHermite: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DFitHermite: Y contains infinite or NAN values!", _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = 1;
|
|
}
|
|
spline1dfithermitewc(x, y, &w, n, &xc, &yc, &dc, 0, m, info, s, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted linear least squares fitting.
|
|
|
|
QR decomposition is used to reduce task to MxM, then triangular solver or
|
|
SVD-based solver is used depending on condition number of the system. It
|
|
allows to maximize speed and retain decent accuracy.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
W - array[0..N-1] Weights corresponding to function values.
|
|
Each summand in square sum of approximation deviations
|
|
from given values is multiplied by the square of
|
|
corresponding weight.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I, J] - value of J-th basis function in I-th point.
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -1 incorrect N/M were specified
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* Rep.TaskRCond reciprocal of condition number
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearw(/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*info = 0;
|
|
ae_vector_clear(c);
|
|
_lsfitreport_clear(rep);
|
|
|
|
ae_assert(n>=1, "LSFitLinearW: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitLinearW: M<1!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitLinearW: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitLinearW: Y contains infinite or NaN values!", _state);
|
|
ae_assert(w->cnt>=n, "LSFitLinearW: length(W)<N!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "LSFitLinearW: W contains infinite or NaN values!", _state);
|
|
ae_assert(fmatrix->rows>=n, "LSFitLinearW: rows(FMatrix)<N!", _state);
|
|
ae_assert(fmatrix->cols>=m, "LSFitLinearW: cols(FMatrix)<M!", _state);
|
|
ae_assert(apservisfinitematrix(fmatrix, n, m, _state), "LSFitLinearW: FMatrix contains infinite or NaN values!", _state);
|
|
lsfit_lsfitlinearinternal(y, w, fmatrix, n, m, info, c, rep, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted constained linear least squares fitting.
|
|
|
|
This is variation of LSFitLinearW(), which searchs for min|A*x=b| given
|
|
that K additional constaints C*x=bc are satisfied. It reduces original
|
|
task to modified one: min|B*y-d| WITHOUT constraints, then LSFitLinearW()
|
|
is called.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
W - array[0..N-1] Weights corresponding to function values.
|
|
Each summand in square sum of approximation deviations
|
|
from given values is multiplied by the square of
|
|
corresponding weight.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I,J] - value of J-th basis function in I-th point.
|
|
CMatrix - a table of constaints, array[0..K-1,0..M].
|
|
I-th row of CMatrix corresponds to I-th linear constraint:
|
|
CMatrix[I,0]*C[0] + ... + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
K - number of constraints, 0 <= K < M
|
|
K=0 corresponds to absence of constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -3 either too many constraints (M or more),
|
|
degenerate constraints (some constraints are
|
|
repetead twice) or inconsistent constraints were
|
|
specified.
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearwc(/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
/* Real */ ae_matrix* cmatrix,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _y;
|
|
ae_matrix _cmatrix;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_vector tau;
|
|
ae_matrix q;
|
|
ae_matrix f2;
|
|
ae_vector tmp;
|
|
ae_vector c0;
|
|
double v;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_matrix_init_copy(&_cmatrix, cmatrix, _state, ae_true);
|
|
cmatrix = &_cmatrix;
|
|
*info = 0;
|
|
ae_vector_clear(c);
|
|
_lsfitreport_clear(rep);
|
|
ae_vector_init(&tau, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&f2, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&c0, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "LSFitLinearWC: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitLinearWC: M<1!", _state);
|
|
ae_assert(k>=0, "LSFitLinearWC: K<0!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitLinearWC: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitLinearWC: Y contains infinite or NaN values!", _state);
|
|
ae_assert(w->cnt>=n, "LSFitLinearWC: length(W)<N!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "LSFitLinearWC: W contains infinite or NaN values!", _state);
|
|
ae_assert(fmatrix->rows>=n, "LSFitLinearWC: rows(FMatrix)<N!", _state);
|
|
ae_assert(fmatrix->cols>=m, "LSFitLinearWC: cols(FMatrix)<M!", _state);
|
|
ae_assert(apservisfinitematrix(fmatrix, n, m, _state), "LSFitLinearWC: FMatrix contains infinite or NaN values!", _state);
|
|
ae_assert(cmatrix->rows>=k, "LSFitLinearWC: rows(CMatrix)<K!", _state);
|
|
ae_assert(cmatrix->cols>=m+1||k==0, "LSFitLinearWC: cols(CMatrix)<M+1!", _state);
|
|
ae_assert(apservisfinitematrix(cmatrix, k, m+1, _state), "LSFitLinearWC: CMatrix contains infinite or NaN values!", _state);
|
|
if( k>=m )
|
|
{
|
|
*info = -3;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Solve
|
|
*/
|
|
if( k==0 )
|
|
{
|
|
|
|
/*
|
|
* no constraints
|
|
*/
|
|
lsfit_lsfitlinearinternal(y, w, fmatrix, n, m, info, c, rep, _state);
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* First, find general form solution of constraints system:
|
|
* * factorize C = L*Q
|
|
* * unpack Q
|
|
* * fill upper part of C with zeros (for RCond)
|
|
*
|
|
* We got C=C0+Q2'*y where Q2 is lower M-K rows of Q.
|
|
*/
|
|
rmatrixlq(cmatrix, k, m, &tau, _state);
|
|
rmatrixlqunpackq(cmatrix, k, m, &tau, m, &q, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=i+1; j<=m-1; j++)
|
|
{
|
|
cmatrix->ptr.pp_double[i][j] = 0.0;
|
|
}
|
|
}
|
|
if( ae_fp_less(rmatrixlurcondinf(cmatrix, k, _state),1000*ae_machineepsilon) )
|
|
{
|
|
*info = -3;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_vector_set_length(&tmp, k, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
if( i>0 )
|
|
{
|
|
v = ae_v_dotproduct(&cmatrix->ptr.pp_double[i][0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,i-1));
|
|
}
|
|
else
|
|
{
|
|
v = 0;
|
|
}
|
|
tmp.ptr.p_double[i] = (cmatrix->ptr.pp_double[i][m]-v)/cmatrix->ptr.pp_double[i][i];
|
|
}
|
|
ae_vector_set_length(&c0, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
c0.ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
v = tmp.ptr.p_double[i];
|
|
ae_v_addd(&c0.ptr.p_double[0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
|
|
}
|
|
|
|
/*
|
|
* Second, prepare modified matrix F2 = F*Q2' and solve modified task
|
|
*/
|
|
ae_vector_set_length(&tmp, ae_maxint(n, m, _state)+1, _state);
|
|
ae_matrix_set_length(&f2, n, m-k, _state);
|
|
matrixvectormultiply(fmatrix, 0, n-1, 0, m-1, ae_false, &c0, 0, m-1, -1.0, y, 0, n-1, 1.0, _state);
|
|
matrixmatrixmultiply(fmatrix, 0, n-1, 0, m-1, ae_false, &q, k, m-1, 0, m-1, ae_true, 1.0, &f2, 0, n-1, 0, m-k-1, 0.0, &tmp, _state);
|
|
lsfit_lsfitlinearinternal(y, w, &f2, n, m-k, info, &tmp, rep, _state);
|
|
rep->taskrcond = -1;
|
|
if( *info<=0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* then, convert back to original answer: C = C0 + Q2'*Y0
|
|
*/
|
|
ae_vector_set_length(c, m, _state);
|
|
ae_v_move(&c->ptr.p_double[0], 1, &c0.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
matrixvectormultiply(&q, k, m-1, 0, m-1, ae_true, &tmp, 0, m-k-1, 1.0, c, 0, m-1, 1.0, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Linear least squares fitting.
|
|
|
|
QR decomposition is used to reduce task to MxM, then triangular solver or
|
|
SVD-based solver is used depending on condition number of the system. It
|
|
allows to maximize speed and retain decent accuracy.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I, J] - value of J-th basis function in I-th point.
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* Rep.TaskRCond reciprocal of condition number
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinear(/* Real */ ae_vector* y,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector w;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
ae_vector_clear(c);
|
|
_lsfitreport_clear(rep);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "LSFitLinear: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitLinear: M<1!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitLinear: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitLinear: Y contains infinite or NaN values!", _state);
|
|
ae_assert(fmatrix->rows>=n, "LSFitLinear: rows(FMatrix)<N!", _state);
|
|
ae_assert(fmatrix->cols>=m, "LSFitLinear: cols(FMatrix)<M!", _state);
|
|
ae_assert(apservisfinitematrix(fmatrix, n, m, _state), "LSFitLinear: FMatrix contains infinite or NaN values!", _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = 1;
|
|
}
|
|
lsfit_lsfitlinearinternal(y, &w, fmatrix, n, m, info, c, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Constained linear least squares fitting.
|
|
|
|
This is variation of LSFitLinear(), which searchs for min|A*x=b| given
|
|
that K additional constaints C*x=bc are satisfied. It reduces original
|
|
task to modified one: min|B*y-d| WITHOUT constraints, then LSFitLinear()
|
|
is called.
|
|
|
|
IMPORTANT: if you want to perform polynomial fitting, it may be more
|
|
convenient to use PolynomialFit() function. This function gives
|
|
best results on polynomial problems and solves numerical
|
|
stability issues which arise when you fit high-degree
|
|
polynomials to your data.
|
|
|
|
INPUT PARAMETERS:
|
|
Y - array[0..N-1] Function values in N points.
|
|
FMatrix - a table of basis functions values, array[0..N-1, 0..M-1].
|
|
FMatrix[I,J] - value of J-th basis function in I-th point.
|
|
CMatrix - a table of constaints, array[0..K-1,0..M].
|
|
I-th row of CMatrix corresponds to I-th linear constraint:
|
|
CMatrix[I,0]*C[0] + ... + CMatrix[I,M-1]*C[M-1] = CMatrix[I,M]
|
|
N - number of points used. N>=1.
|
|
M - number of basis functions, M>=1.
|
|
K - number of constraints, 0 <= K < M
|
|
K=0 corresponds to absence of constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - error code:
|
|
* -4 internal SVD decomposition subroutine failed (very
|
|
rare and for degenerate systems only)
|
|
* -3 either too many constraints (M or more),
|
|
degenerate constraints (some constraints are
|
|
repetead twice) or inconsistent constraints were
|
|
specified.
|
|
* 1 task is solved
|
|
C - decomposition coefficients, array[0..M-1]
|
|
Rep - fitting report. Following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(F*CovPar*F')),
|
|
where F is functions matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitlinearc(/* Real */ ae_vector* y,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
/* Real */ ae_matrix* cmatrix,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _y;
|
|
ae_vector w;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
*info = 0;
|
|
ae_vector_clear(c);
|
|
_lsfitreport_clear(rep);
|
|
ae_vector_init(&w, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "LSFitLinearC: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitLinearC: M<1!", _state);
|
|
ae_assert(k>=0, "LSFitLinearC: K<0!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitLinearC: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitLinearC: Y contains infinite or NaN values!", _state);
|
|
ae_assert(fmatrix->rows>=n, "LSFitLinearC: rows(FMatrix)<N!", _state);
|
|
ae_assert(fmatrix->cols>=m, "LSFitLinearC: cols(FMatrix)<M!", _state);
|
|
ae_assert(apservisfinitematrix(fmatrix, n, m, _state), "LSFitLinearC: FMatrix contains infinite or NaN values!", _state);
|
|
ae_assert(cmatrix->rows>=k, "LSFitLinearC: rows(CMatrix)<K!", _state);
|
|
ae_assert(cmatrix->cols>=m+1||k==0, "LSFitLinearC: cols(CMatrix)<M+1!", _state);
|
|
ae_assert(apservisfinitematrix(cmatrix, k, m+1, _state), "LSFitLinearC: CMatrix contains infinite or NaN values!", _state);
|
|
ae_vector_set_length(&w, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w.ptr.p_double[i] = 1;
|
|
}
|
|
lsfitlinearwc(y, &w, fmatrix, cmatrix, n, m, k, info, c, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using function values only.
|
|
|
|
Combination of numerical differentiation and secant updates is used to
|
|
obtain function Jacobian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]).
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
DiffStep- numerical differentiation step;
|
|
should not be very small or large;
|
|
large = loss of accuracy
|
|
small = growth of round-off errors
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 18.10.2008 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewf(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_vector* c,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
double diffstep,
|
|
lsfitstate* state,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
_lsfitstate_clear(state);
|
|
|
|
ae_assert(n>=1, "LSFitCreateWF: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitCreateWF: M<1!", _state);
|
|
ae_assert(k>=1, "LSFitCreateWF: K<1!", _state);
|
|
ae_assert(c->cnt>=k, "LSFitCreateWF: length(C)<K!", _state);
|
|
ae_assert(isfinitevector(c, k, _state), "LSFitCreateWF: C contains infinite or NaN values!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitCreateWF: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitCreateWF: Y contains infinite or NaN values!", _state);
|
|
ae_assert(w->cnt>=n, "LSFitCreateWF: length(W)<N!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "LSFitCreateWF: W contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateWF: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateWF: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateWF: X contains infinite or NaN values!", _state);
|
|
ae_assert(ae_isfinite(diffstep, _state), "LSFitCreateWF: DiffStep is not finite!", _state);
|
|
ae_assert(ae_fp_greater(diffstep,0), "LSFitCreateWF: DiffStep<=0!", _state);
|
|
state->teststep = 0;
|
|
state->diffstep = diffstep;
|
|
state->npoints = n;
|
|
state->nweights = n;
|
|
state->wkind = 1;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 0.0, 0.0, 0, _state);
|
|
lsfitsetstpmax(state, 0.0, _state);
|
|
lsfitsetxrep(state, ae_false, _state);
|
|
ae_matrix_set_length(&state->taskx, n, m, _state);
|
|
ae_vector_set_length(&state->tasky, n, _state);
|
|
ae_vector_set_length(&state->taskw, n, _state);
|
|
ae_vector_set_length(&state->c, k, _state);
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->taskw.ptr.p_double[0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_v_move(&state->taskx.ptr.pp_double[i][0], 1, &x->ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->tasky.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
ae_vector_set_length(&state->s, k, _state);
|
|
ae_vector_set_length(&state->bndl, k, _state);
|
|
ae_vector_set_length(&state->bndu, k, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->s.ptr.p_double[i] = 1.0;
|
|
state->bndl.ptr.p_double[i] = _state->v_neginf;
|
|
state->bndu.ptr.p_double[i] = _state->v_posinf;
|
|
}
|
|
state->optalgo = 0;
|
|
state->prevnpt = -1;
|
|
state->prevalgo = -1;
|
|
minlmcreatev(k, n, &state->c, diffstep, &state->optstate, _state);
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
ae_vector_set_length(&state->rstate.ia, 6+1, _state);
|
|
ae_vector_set_length(&state->rstate.ra, 8+1, _state);
|
|
state->rstate.stage = -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using function values only.
|
|
|
|
Combination of numerical differentiation and secant updates is used to
|
|
obtain function Jacobian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (f(c,x[0])-y[0])^2 + ... + (f(c,x[n-1])-y[n-1])^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]).
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
DiffStep- numerical differentiation step;
|
|
should not be very small or large;
|
|
large = loss of accuracy
|
|
small = growth of round-off errors
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 18.10.2008 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatef(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* c,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
double diffstep,
|
|
lsfitstate* state,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
_lsfitstate_clear(state);
|
|
|
|
ae_assert(n>=1, "LSFitCreateF: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitCreateF: M<1!", _state);
|
|
ae_assert(k>=1, "LSFitCreateF: K<1!", _state);
|
|
ae_assert(c->cnt>=k, "LSFitCreateF: length(C)<K!", _state);
|
|
ae_assert(isfinitevector(c, k, _state), "LSFitCreateF: C contains infinite or NaN values!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitCreateF: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitCreateF: Y contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateF: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateF: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateF: X contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateF: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateF: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateF: X contains infinite or NaN values!", _state);
|
|
ae_assert(ae_isfinite(diffstep, _state), "LSFitCreateF: DiffStep is not finite!", _state);
|
|
ae_assert(ae_fp_greater(diffstep,0), "LSFitCreateF: DiffStep<=0!", _state);
|
|
state->teststep = 0;
|
|
state->diffstep = diffstep;
|
|
state->npoints = n;
|
|
state->wkind = 0;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 0.0, 0.0, 0, _state);
|
|
lsfitsetstpmax(state, 0.0, _state);
|
|
lsfitsetxrep(state, ae_false, _state);
|
|
ae_matrix_set_length(&state->taskx, n, m, _state);
|
|
ae_vector_set_length(&state->tasky, n, _state);
|
|
ae_vector_set_length(&state->c, k, _state);
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_v_move(&state->taskx.ptr.pp_double[i][0], 1, &x->ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->tasky.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
ae_vector_set_length(&state->s, k, _state);
|
|
ae_vector_set_length(&state->bndl, k, _state);
|
|
ae_vector_set_length(&state->bndu, k, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->s.ptr.p_double[i] = 1.0;
|
|
state->bndl.ptr.p_double[i] = _state->v_neginf;
|
|
state->bndu.ptr.p_double[i] = _state->v_posinf;
|
|
}
|
|
state->optalgo = 0;
|
|
state->prevnpt = -1;
|
|
state->prevalgo = -1;
|
|
minlmcreatev(k, n, &state->c, diffstep, &state->optstate, _state);
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
ae_vector_set_length(&state->rstate.ia, 6+1, _state);
|
|
ae_vector_set_length(&state->rstate.ra, 8+1, _state);
|
|
state->rstate.stage = -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using gradient only.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]) and its gradient.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
CheapFG - boolean flag, which is:
|
|
* True if both function and gradient calculation complexity
|
|
are less than O(M^2). An improved algorithm can
|
|
be used which corresponds to FGJ scheme from
|
|
MINLM unit.
|
|
* False otherwise.
|
|
Standard Jacibian-bases Levenberg-Marquardt algo
|
|
will be used (FJ scheme).
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
See also:
|
|
LSFitResults
|
|
LSFitCreateFG (fitting without weights)
|
|
LSFitCreateWFGH (fitting using Hessian)
|
|
LSFitCreateFGH (fitting using Hessian, without weights)
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewfg(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_vector* c,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
ae_bool cheapfg,
|
|
lsfitstate* state,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
_lsfitstate_clear(state);
|
|
|
|
ae_assert(n>=1, "LSFitCreateWFG: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitCreateWFG: M<1!", _state);
|
|
ae_assert(k>=1, "LSFitCreateWFG: K<1!", _state);
|
|
ae_assert(c->cnt>=k, "LSFitCreateWFG: length(C)<K!", _state);
|
|
ae_assert(isfinitevector(c, k, _state), "LSFitCreateWFG: C contains infinite or NaN values!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitCreateWFG: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitCreateWFG: Y contains infinite or NaN values!", _state);
|
|
ae_assert(w->cnt>=n, "LSFitCreateWFG: length(W)<N!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "LSFitCreateWFG: W contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateWFG: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateWFG: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateWFG: X contains infinite or NaN values!", _state);
|
|
state->teststep = 0;
|
|
state->diffstep = 0;
|
|
state->npoints = n;
|
|
state->nweights = n;
|
|
state->wkind = 1;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 0.0, 0.0, 0, _state);
|
|
lsfitsetstpmax(state, 0.0, _state);
|
|
lsfitsetxrep(state, ae_false, _state);
|
|
ae_matrix_set_length(&state->taskx, n, m, _state);
|
|
ae_vector_set_length(&state->tasky, n, _state);
|
|
ae_vector_set_length(&state->taskw, n, _state);
|
|
ae_vector_set_length(&state->c, k, _state);
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_vector_set_length(&state->g, k, _state);
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->taskw.ptr.p_double[0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_v_move(&state->taskx.ptr.pp_double[i][0], 1, &x->ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->tasky.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
ae_vector_set_length(&state->s, k, _state);
|
|
ae_vector_set_length(&state->bndl, k, _state);
|
|
ae_vector_set_length(&state->bndu, k, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->s.ptr.p_double[i] = 1.0;
|
|
state->bndl.ptr.p_double[i] = _state->v_neginf;
|
|
state->bndu.ptr.p_double[i] = _state->v_posinf;
|
|
}
|
|
state->optalgo = 1;
|
|
state->prevnpt = -1;
|
|
state->prevalgo = -1;
|
|
if( cheapfg )
|
|
{
|
|
minlmcreatevgj(k, n, &state->c, &state->optstate, _state);
|
|
}
|
|
else
|
|
{
|
|
minlmcreatevj(k, n, &state->c, &state->optstate, _state);
|
|
}
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
ae_vector_set_length(&state->rstate.ia, 6+1, _state);
|
|
ae_vector_set_length(&state->rstate.ra, 8+1, _state);
|
|
state->rstate.stage = -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using gradient only, without individual
|
|
weights.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses only f(c,x[i]) and its gradient.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
CheapFG - boolean flag, which is:
|
|
* True if both function and gradient calculation complexity
|
|
are less than O(M^2). An improved algorithm can
|
|
be used which corresponds to FGJ scheme from
|
|
MINLM unit.
|
|
* False otherwise.
|
|
Standard Jacibian-bases Levenberg-Marquardt algo
|
|
will be used (FJ scheme).
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatefg(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* c,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
ae_bool cheapfg,
|
|
lsfitstate* state,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
_lsfitstate_clear(state);
|
|
|
|
ae_assert(n>=1, "LSFitCreateFG: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitCreateFG: M<1!", _state);
|
|
ae_assert(k>=1, "LSFitCreateFG: K<1!", _state);
|
|
ae_assert(c->cnt>=k, "LSFitCreateFG: length(C)<K!", _state);
|
|
ae_assert(isfinitevector(c, k, _state), "LSFitCreateFG: C contains infinite or NaN values!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitCreateFG: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitCreateFG: Y contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateFG: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateFG: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateFG: X contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateFG: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateFG: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateFG: X contains infinite or NaN values!", _state);
|
|
state->teststep = 0;
|
|
state->diffstep = 0;
|
|
state->npoints = n;
|
|
state->wkind = 0;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 0.0, 0.0, 0, _state);
|
|
lsfitsetstpmax(state, 0.0, _state);
|
|
lsfitsetxrep(state, ae_false, _state);
|
|
ae_matrix_set_length(&state->taskx, n, m, _state);
|
|
ae_vector_set_length(&state->tasky, n, _state);
|
|
ae_vector_set_length(&state->c, k, _state);
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_vector_set_length(&state->g, k, _state);
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_v_move(&state->taskx.ptr.pp_double[i][0], 1, &x->ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->tasky.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
ae_vector_set_length(&state->s, k, _state);
|
|
ae_vector_set_length(&state->bndl, k, _state);
|
|
ae_vector_set_length(&state->bndu, k, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->s.ptr.p_double[i] = 1.0;
|
|
state->bndl.ptr.p_double[i] = _state->v_neginf;
|
|
state->bndu.ptr.p_double[i] = _state->v_posinf;
|
|
}
|
|
state->optalgo = 1;
|
|
state->prevnpt = -1;
|
|
state->prevalgo = -1;
|
|
if( cheapfg )
|
|
{
|
|
minlmcreatevgj(k, n, &state->c, &state->optstate, _state);
|
|
}
|
|
else
|
|
{
|
|
minlmcreatevj(k, n, &state->c, &state->optstate, _state);
|
|
}
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
ae_vector_set_length(&state->rstate.ia, 6+1, _state);
|
|
ae_vector_set_length(&state->rstate.ra, 8+1, _state);
|
|
state->rstate.stage = -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Weighted nonlinear least squares fitting using gradient/Hessian.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = (w[0]*(f(c,x[0])-y[0]))^2 + ... + (w[n-1]*(f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* w is an N-dimensional vector of weight coefficients,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses f(c,x[i]), its gradient and its Hessian.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
W - weights, array[0..N-1]
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatewfgh(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_vector* c,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
lsfitstate* state,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
_lsfitstate_clear(state);
|
|
|
|
ae_assert(n>=1, "LSFitCreateWFGH: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitCreateWFGH: M<1!", _state);
|
|
ae_assert(k>=1, "LSFitCreateWFGH: K<1!", _state);
|
|
ae_assert(c->cnt>=k, "LSFitCreateWFGH: length(C)<K!", _state);
|
|
ae_assert(isfinitevector(c, k, _state), "LSFitCreateWFGH: C contains infinite or NaN values!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitCreateWFGH: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitCreateWFGH: Y contains infinite or NaN values!", _state);
|
|
ae_assert(w->cnt>=n, "LSFitCreateWFGH: length(W)<N!", _state);
|
|
ae_assert(isfinitevector(w, n, _state), "LSFitCreateWFGH: W contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateWFGH: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateWFGH: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateWFGH: X contains infinite or NaN values!", _state);
|
|
state->teststep = 0;
|
|
state->diffstep = 0;
|
|
state->npoints = n;
|
|
state->nweights = n;
|
|
state->wkind = 1;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 0.0, 0.0, 0, _state);
|
|
lsfitsetstpmax(state, 0.0, _state);
|
|
lsfitsetxrep(state, ae_false, _state);
|
|
ae_matrix_set_length(&state->taskx, n, m, _state);
|
|
ae_vector_set_length(&state->tasky, n, _state);
|
|
ae_vector_set_length(&state->taskw, n, _state);
|
|
ae_vector_set_length(&state->c, k, _state);
|
|
ae_matrix_set_length(&state->h, k, k, _state);
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_vector_set_length(&state->g, k, _state);
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->taskw.ptr.p_double[0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_v_move(&state->taskx.ptr.pp_double[i][0], 1, &x->ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->tasky.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
ae_vector_set_length(&state->s, k, _state);
|
|
ae_vector_set_length(&state->bndl, k, _state);
|
|
ae_vector_set_length(&state->bndu, k, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->s.ptr.p_double[i] = 1.0;
|
|
state->bndl.ptr.p_double[i] = _state->v_neginf;
|
|
state->bndu.ptr.p_double[i] = _state->v_posinf;
|
|
}
|
|
state->optalgo = 2;
|
|
state->prevnpt = -1;
|
|
state->prevalgo = -1;
|
|
minlmcreatefgh(k, &state->c, &state->optstate, _state);
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
ae_vector_set_length(&state->rstate.ia, 6+1, _state);
|
|
ae_vector_set_length(&state->rstate.ra, 8+1, _state);
|
|
state->rstate.stage = -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting using gradient/Hessian, without individial
|
|
weights.
|
|
|
|
Nonlinear task min(F(c)) is solved, where
|
|
|
|
F(c) = ((f(c,x[0])-y[0]))^2 + ... + ((f(c,x[n-1])-y[n-1]))^2,
|
|
|
|
* N is a number of points,
|
|
* M is a dimension of a space points belong to,
|
|
* K is a dimension of a space of parameters being fitted,
|
|
* x is a set of N points, each of them is an M-dimensional vector,
|
|
* c is a K-dimensional vector of parameters being fitted
|
|
|
|
This subroutine uses f(c,x[i]), its gradient and its Hessian.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[0..N-1,0..M-1], points (one row = one point)
|
|
Y - array[0..N-1], function values.
|
|
C - array[0..K-1], initial approximation to the solution,
|
|
N - number of points, N>1
|
|
M - dimension of space
|
|
K - number of parameters being fitted
|
|
|
|
OUTPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitcreatefgh(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* c,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t k,
|
|
lsfitstate* state,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
_lsfitstate_clear(state);
|
|
|
|
ae_assert(n>=1, "LSFitCreateFGH: N<1!", _state);
|
|
ae_assert(m>=1, "LSFitCreateFGH: M<1!", _state);
|
|
ae_assert(k>=1, "LSFitCreateFGH: K<1!", _state);
|
|
ae_assert(c->cnt>=k, "LSFitCreateFGH: length(C)<K!", _state);
|
|
ae_assert(isfinitevector(c, k, _state), "LSFitCreateFGH: C contains infinite or NaN values!", _state);
|
|
ae_assert(y->cnt>=n, "LSFitCreateFGH: length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LSFitCreateFGH: Y contains infinite or NaN values!", _state);
|
|
ae_assert(x->rows>=n, "LSFitCreateFGH: rows(X)<N!", _state);
|
|
ae_assert(x->cols>=m, "LSFitCreateFGH: cols(X)<M!", _state);
|
|
ae_assert(apservisfinitematrix(x, n, m, _state), "LSFitCreateFGH: X contains infinite or NaN values!", _state);
|
|
state->teststep = 0;
|
|
state->diffstep = 0;
|
|
state->npoints = n;
|
|
state->wkind = 0;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 0.0, 0.0, 0, _state);
|
|
lsfitsetstpmax(state, 0.0, _state);
|
|
lsfitsetxrep(state, ae_false, _state);
|
|
ae_matrix_set_length(&state->taskx, n, m, _state);
|
|
ae_vector_set_length(&state->tasky, n, _state);
|
|
ae_vector_set_length(&state->c, k, _state);
|
|
ae_matrix_set_length(&state->h, k, k, _state);
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_vector_set_length(&state->g, k, _state);
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_v_move(&state->taskx.ptr.pp_double[i][0], 1, &x->ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->tasky.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
ae_vector_set_length(&state->s, k, _state);
|
|
ae_vector_set_length(&state->bndl, k, _state);
|
|
ae_vector_set_length(&state->bndu, k, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->s.ptr.p_double[i] = 1.0;
|
|
state->bndl.ptr.p_double[i] = _state->v_neginf;
|
|
state->bndu.ptr.p_double[i] = _state->v_posinf;
|
|
}
|
|
state->optalgo = 2;
|
|
state->prevnpt = -1;
|
|
state->prevalgo = -1;
|
|
minlmcreatefgh(k, &state->c, &state->optstate, _state);
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
ae_vector_set_length(&state->rstate.ia, 6+1, _state);
|
|
ae_vector_set_length(&state->rstate.ra, 8+1, _state);
|
|
state->rstate.stage = -1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Stopping conditions for nonlinear least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
EpsF - stopping criterion. Algorithm stops if
|
|
|F(k+1)-F(k)| <= EpsF*max{|F(k)|, |F(k+1)|, 1}
|
|
EpsX - >=0
|
|
The subroutine finishes its work if on k+1-th iteration
|
|
the condition |v|<=EpsX is fulfilled, where:
|
|
* |.| means Euclidian norm
|
|
* v - scaled step vector, v[i]=dx[i]/s[i]
|
|
* dx - ste pvector, dx=X(k+1)-X(k)
|
|
* s - scaling coefficients set by LSFitSetScale()
|
|
MaxIts - maximum number of iterations. If MaxIts=0, the number of
|
|
iterations is unlimited. Only Levenberg-Marquardt
|
|
iterations are counted (L-BFGS/CG iterations are NOT
|
|
counted because their cost is very low compared to that of
|
|
LM).
|
|
|
|
NOTE
|
|
|
|
Passing EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will lead to automatic
|
|
stopping criterion selection (according to the scheme used by MINLM unit).
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetcond(lsfitstate* state,
|
|
double epsf,
|
|
double epsx,
|
|
ae_int_t maxits,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(epsf, _state), "LSFitSetCond: EpsF is not finite!", _state);
|
|
ae_assert(ae_fp_greater_eq(epsf,0), "LSFitSetCond: negative EpsF!", _state);
|
|
ae_assert(ae_isfinite(epsx, _state), "LSFitSetCond: EpsX is not finite!", _state);
|
|
ae_assert(ae_fp_greater_eq(epsx,0), "LSFitSetCond: negative EpsX!", _state);
|
|
ae_assert(maxits>=0, "LSFitSetCond: negative MaxIts!", _state);
|
|
state->epsf = epsf;
|
|
state->epsx = epsx;
|
|
state->maxits = maxits;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets maximum step length
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
StpMax - maximum step length, >=0. Set StpMax to 0.0, if you don't
|
|
want to limit step length.
|
|
|
|
Use this subroutine when you optimize target function which contains exp()
|
|
or other fast growing functions, and optimization algorithm makes too
|
|
large steps which leads to overflow. This function allows us to reject
|
|
steps that are too large (and therefore expose us to the possible
|
|
overflow) without actually calculating function value at the x+stp*d.
|
|
|
|
NOTE: non-zero StpMax leads to moderate performance degradation because
|
|
intermediate step of preconditioned L-BFGS optimization is incompatible
|
|
with limits on step size.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.04.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetstpmax(lsfitstate* state, double stpmax, ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_fp_greater_eq(stpmax,0), "LSFitSetStpMax: StpMax<0!", _state);
|
|
state->stpmax = stpmax;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function turns on/off reporting.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
NeedXRep- whether iteration reports are needed or not
|
|
|
|
When reports are needed, State.C (current parameters) and State.F (current
|
|
value of fitting function) are reported.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 15.08.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetxrep(lsfitstate* state, ae_bool needxrep, ae_state *_state)
|
|
{
|
|
|
|
|
|
state->xrep = needxrep;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets scaling coefficients for underlying optimizer.
|
|
|
|
ALGLIB optimizers use scaling matrices to test stopping conditions (step
|
|
size and gradient are scaled before comparison with tolerances). Scale of
|
|
the I-th variable is a translation invariant measure of:
|
|
a) "how large" the variable is
|
|
b) how large the step should be to make significant changes in the function
|
|
|
|
Generally, scale is NOT considered to be a form of preconditioner. But LM
|
|
optimizer is unique in that it uses scaling matrix both in the stopping
|
|
condition tests and as Marquardt damping factor.
|
|
|
|
Proper scaling is very important for the algorithm performance. It is less
|
|
important for the quality of results, but still has some influence (it is
|
|
easier to converge when variables are properly scaled, so premature
|
|
stopping is possible when very badly scalled variables are combined with
|
|
relaxed stopping conditions).
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure stores algorithm state
|
|
S - array[N], non-zero scaling coefficients
|
|
S[i] may be negative, sign doesn't matter.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.01.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetscale(lsfitstate* state,
|
|
/* Real */ ae_vector* s,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
|
|
ae_assert(s->cnt>=state->k, "LSFitSetScale: Length(S)<K", _state);
|
|
for(i=0; i<=state->k-1; i++)
|
|
{
|
|
ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "LSFitSetScale: S contains infinite or NAN elements", _state);
|
|
ae_assert(ae_fp_neq(s->ptr.p_double[i],0), "LSFitSetScale: S contains infinite or NAN elements", _state);
|
|
state->s.ptr.p_double[i] = ae_fabs(s->ptr.p_double[i], _state);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets boundary constraints for underlying optimizer
|
|
|
|
Boundary constraints are inactive by default (after initial creation).
|
|
They are preserved until explicitly turned off with another SetBC() call.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure stores algorithm state
|
|
BndL - lower bounds, array[K].
|
|
If some (all) variables are unbounded, you may specify
|
|
very small number or -INF (latter is recommended because
|
|
it will allow solver to use better algorithm).
|
|
BndU - upper bounds, array[K].
|
|
If some (all) variables are unbounded, you may specify
|
|
very large number or +INF (latter is recommended because
|
|
it will allow solver to use better algorithm).
|
|
|
|
NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case I-th
|
|
variable will be "frozen" at X[i]=BndL[i]=BndU[i].
|
|
|
|
NOTE 2: unlike other constrained optimization algorithms, this solver has
|
|
following useful properties:
|
|
* bound constraints are always satisfied exactly
|
|
* function is evaluated only INSIDE area specified by bound constraints
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.01.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetbc(lsfitstate* state,
|
|
/* Real */ ae_vector* bndl,
|
|
/* Real */ ae_vector* bndu,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
|
|
|
|
k = state->k;
|
|
ae_assert(bndl->cnt>=k, "LSFitSetBC: Length(BndL)<K", _state);
|
|
ae_assert(bndu->cnt>=k, "LSFitSetBC: Length(BndU)<K", _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(ae_isfinite(bndl->ptr.p_double[i], _state)||ae_isneginf(bndl->ptr.p_double[i], _state), "LSFitSetBC: BndL contains NAN or +INF", _state);
|
|
ae_assert(ae_isfinite(bndu->ptr.p_double[i], _state)||ae_isposinf(bndu->ptr.p_double[i], _state), "LSFitSetBC: BndU contains NAN or -INF", _state);
|
|
if( ae_isfinite(bndl->ptr.p_double[i], _state)&&ae_isfinite(bndu->ptr.p_double[i], _state) )
|
|
{
|
|
ae_assert(ae_fp_less_eq(bndl->ptr.p_double[i],bndu->ptr.p_double[i]), "LSFitSetBC: BndL[i]>BndU[i]", _state);
|
|
}
|
|
state->bndl.ptr.p_double[i] = bndl->ptr.p_double[i];
|
|
state->bndu.ptr.p_double[i] = bndu->ptr.p_double[i];
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
NOTES:
|
|
|
|
1. this algorithm is somewhat unusual because it works with parameterized
|
|
function f(C,X), where X is a function argument (we have many points
|
|
which are characterized by different argument values), and C is a
|
|
parameter to fit.
|
|
|
|
For example, if we want to do linear fit by f(c0,c1,x) = c0*x+c1, then
|
|
x will be argument, and {c0,c1} will be parameters.
|
|
|
|
It is important to understand that this algorithm finds minimum in the
|
|
space of function PARAMETERS (not arguments), so it needs derivatives
|
|
of f() with respect to C, not X.
|
|
|
|
In the example above it will need f=c0*x+c1 and {df/dc0,df/dc1} = {x,1}
|
|
instead of {df/dx} = {c0}.
|
|
|
|
2. Callback functions accept C as the first parameter, and X as the second
|
|
|
|
3. If state was created with LSFitCreateFG(), algorithm needs just
|
|
function and its gradient, but if state was created with
|
|
LSFitCreateFGH(), algorithm will need function, gradient and Hessian.
|
|
|
|
According to the said above, there ase several versions of this
|
|
function, which accept different sets of callbacks.
|
|
|
|
This flexibility opens way to subtle errors - you may create state with
|
|
LSFitCreateFGH() (optimization using Hessian), but call function which
|
|
does not accept Hessian. So when algorithm will request Hessian, there
|
|
will be no callback to call. In this case exception will be thrown.
|
|
|
|
Be careful to avoid such errors because there is no way to find them at
|
|
compile time - you can see them at runtime only.
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
ae_bool lsfititeration(lsfitstate* state, ae_state *_state)
|
|
{
|
|
double lx;
|
|
double lf;
|
|
double ld;
|
|
double rx;
|
|
double rf;
|
|
double rd;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
double v;
|
|
double vv;
|
|
double relcnt;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t j1;
|
|
ae_int_t info;
|
|
ae_bool result;
|
|
|
|
|
|
|
|
/*
|
|
* Reverse communication preparations
|
|
* I know it looks ugly, but it works the same way
|
|
* anywhere from C++ to Python.
|
|
*
|
|
* This code initializes locals by:
|
|
* * random values determined during code
|
|
* generation - on first subroutine call
|
|
* * values from previous call - on subsequent calls
|
|
*/
|
|
if( state->rstate.stage>=0 )
|
|
{
|
|
n = state->rstate.ia.ptr.p_int[0];
|
|
m = state->rstate.ia.ptr.p_int[1];
|
|
k = state->rstate.ia.ptr.p_int[2];
|
|
i = state->rstate.ia.ptr.p_int[3];
|
|
j = state->rstate.ia.ptr.p_int[4];
|
|
j1 = state->rstate.ia.ptr.p_int[5];
|
|
info = state->rstate.ia.ptr.p_int[6];
|
|
lx = state->rstate.ra.ptr.p_double[0];
|
|
lf = state->rstate.ra.ptr.p_double[1];
|
|
ld = state->rstate.ra.ptr.p_double[2];
|
|
rx = state->rstate.ra.ptr.p_double[3];
|
|
rf = state->rstate.ra.ptr.p_double[4];
|
|
rd = state->rstate.ra.ptr.p_double[5];
|
|
v = state->rstate.ra.ptr.p_double[6];
|
|
vv = state->rstate.ra.ptr.p_double[7];
|
|
relcnt = state->rstate.ra.ptr.p_double[8];
|
|
}
|
|
else
|
|
{
|
|
n = -983;
|
|
m = -989;
|
|
k = -834;
|
|
i = 900;
|
|
j = -287;
|
|
j1 = 364;
|
|
info = 214;
|
|
lx = -338;
|
|
lf = -686;
|
|
ld = 912;
|
|
rx = 585;
|
|
rf = 497;
|
|
rd = -271;
|
|
v = -581;
|
|
vv = 745;
|
|
relcnt = -533;
|
|
}
|
|
if( state->rstate.stage==0 )
|
|
{
|
|
goto lbl_0;
|
|
}
|
|
if( state->rstate.stage==1 )
|
|
{
|
|
goto lbl_1;
|
|
}
|
|
if( state->rstate.stage==2 )
|
|
{
|
|
goto lbl_2;
|
|
}
|
|
if( state->rstate.stage==3 )
|
|
{
|
|
goto lbl_3;
|
|
}
|
|
if( state->rstate.stage==4 )
|
|
{
|
|
goto lbl_4;
|
|
}
|
|
if( state->rstate.stage==5 )
|
|
{
|
|
goto lbl_5;
|
|
}
|
|
if( state->rstate.stage==6 )
|
|
{
|
|
goto lbl_6;
|
|
}
|
|
if( state->rstate.stage==7 )
|
|
{
|
|
goto lbl_7;
|
|
}
|
|
if( state->rstate.stage==8 )
|
|
{
|
|
goto lbl_8;
|
|
}
|
|
if( state->rstate.stage==9 )
|
|
{
|
|
goto lbl_9;
|
|
}
|
|
if( state->rstate.stage==10 )
|
|
{
|
|
goto lbl_10;
|
|
}
|
|
if( state->rstate.stage==11 )
|
|
{
|
|
goto lbl_11;
|
|
}
|
|
if( state->rstate.stage==12 )
|
|
{
|
|
goto lbl_12;
|
|
}
|
|
if( state->rstate.stage==13 )
|
|
{
|
|
goto lbl_13;
|
|
}
|
|
|
|
/*
|
|
* Routine body
|
|
*/
|
|
|
|
/*
|
|
* Init
|
|
*/
|
|
if( state->wkind==1 )
|
|
{
|
|
ae_assert(state->npoints==state->nweights, "LSFitFit: number of points is not equal to the number of weights", _state);
|
|
}
|
|
state->repvaridx = -1;
|
|
n = state->npoints;
|
|
m = state->m;
|
|
k = state->k;
|
|
minlmsetcond(&state->optstate, 0.0, state->epsf, state->epsx, state->maxits, _state);
|
|
minlmsetstpmax(&state->optstate, state->stpmax, _state);
|
|
minlmsetxrep(&state->optstate, state->xrep, _state);
|
|
minlmsetscale(&state->optstate, &state->s, _state);
|
|
minlmsetbc(&state->optstate, &state->bndl, &state->bndu, _state);
|
|
|
|
/*
|
|
* Check that user-supplied gradient is correct
|
|
*/
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
if( !(ae_fp_greater(state->teststep,0)&&state->optalgo==1) )
|
|
{
|
|
goto lbl_14;
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
if( ae_isfinite(state->bndl.ptr.p_double[i], _state) )
|
|
{
|
|
state->c.ptr.p_double[i] = ae_maxreal(state->c.ptr.p_double[i], state->bndl.ptr.p_double[i], _state);
|
|
}
|
|
if( ae_isfinite(state->bndu.ptr.p_double[i], _state) )
|
|
{
|
|
state->c.ptr.p_double[i] = ae_minreal(state->c.ptr.p_double[i], state->bndu.ptr.p_double[i], _state);
|
|
}
|
|
}
|
|
state->needfg = ae_true;
|
|
i = 0;
|
|
lbl_16:
|
|
if( i>k-1 )
|
|
{
|
|
goto lbl_18;
|
|
}
|
|
ae_assert(ae_fp_less_eq(state->bndl.ptr.p_double[i],state->c.ptr.p_double[i])&&ae_fp_less_eq(state->c.ptr.p_double[i],state->bndu.ptr.p_double[i]), "LSFitIteration: internal error(State.C is out of bounds)", _state);
|
|
v = state->c.ptr.p_double[i];
|
|
j = 0;
|
|
lbl_19:
|
|
if( j>n-1 )
|
|
{
|
|
goto lbl_21;
|
|
}
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[j][0], 1, ae_v_len(0,m-1));
|
|
state->c.ptr.p_double[i] = v-state->teststep*state->s.ptr.p_double[i];
|
|
if( ae_isfinite(state->bndl.ptr.p_double[i], _state) )
|
|
{
|
|
state->c.ptr.p_double[i] = ae_maxreal(state->c.ptr.p_double[i], state->bndl.ptr.p_double[i], _state);
|
|
}
|
|
lx = state->c.ptr.p_double[i];
|
|
state->rstate.stage = 0;
|
|
goto lbl_rcomm;
|
|
lbl_0:
|
|
lf = state->f;
|
|
ld = state->g.ptr.p_double[i];
|
|
state->c.ptr.p_double[i] = v+state->teststep*state->s.ptr.p_double[i];
|
|
if( ae_isfinite(state->bndu.ptr.p_double[i], _state) )
|
|
{
|
|
state->c.ptr.p_double[i] = ae_minreal(state->c.ptr.p_double[i], state->bndu.ptr.p_double[i], _state);
|
|
}
|
|
rx = state->c.ptr.p_double[i];
|
|
state->rstate.stage = 1;
|
|
goto lbl_rcomm;
|
|
lbl_1:
|
|
rf = state->f;
|
|
rd = state->g.ptr.p_double[i];
|
|
state->c.ptr.p_double[i] = (lx+rx)/2;
|
|
if( ae_isfinite(state->bndl.ptr.p_double[i], _state) )
|
|
{
|
|
state->c.ptr.p_double[i] = ae_maxreal(state->c.ptr.p_double[i], state->bndl.ptr.p_double[i], _state);
|
|
}
|
|
if( ae_isfinite(state->bndu.ptr.p_double[i], _state) )
|
|
{
|
|
state->c.ptr.p_double[i] = ae_minreal(state->c.ptr.p_double[i], state->bndu.ptr.p_double[i], _state);
|
|
}
|
|
state->rstate.stage = 2;
|
|
goto lbl_rcomm;
|
|
lbl_2:
|
|
state->c.ptr.p_double[i] = v;
|
|
if( !derivativecheck(lf, ld, rf, rd, state->f, state->g.ptr.p_double[i], rx-lx, _state) )
|
|
{
|
|
state->repvaridx = i;
|
|
state->repterminationtype = -7;
|
|
result = ae_false;
|
|
return result;
|
|
}
|
|
j = j+1;
|
|
goto lbl_19;
|
|
lbl_21:
|
|
i = i+1;
|
|
goto lbl_16;
|
|
lbl_18:
|
|
state->needfg = ae_false;
|
|
lbl_14:
|
|
|
|
/*
|
|
* Fill WCur by weights:
|
|
* * for WKind=0 unit weights are chosen
|
|
* * for WKind=1 we use user-supplied weights stored in State.TaskW
|
|
*/
|
|
rvectorsetlengthatleast(&state->wcur, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
state->wcur.ptr.p_double[i] = 1.0;
|
|
if( state->wkind==1 )
|
|
{
|
|
state->wcur.ptr.p_double[i] = state->taskw.ptr.p_double[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Optimize
|
|
*/
|
|
lbl_22:
|
|
if( !minlmiteration(&state->optstate, _state) )
|
|
{
|
|
goto lbl_23;
|
|
}
|
|
if( !state->optstate.needfi )
|
|
{
|
|
goto lbl_24;
|
|
}
|
|
|
|
/*
|
|
* calculate f[] = wi*(f(xi,c)-yi)
|
|
*/
|
|
i = 0;
|
|
lbl_26:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_28;
|
|
}
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->optstate.x.ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needf = ae_true;
|
|
state->rstate.stage = 3;
|
|
goto lbl_rcomm;
|
|
lbl_3:
|
|
state->needf = ae_false;
|
|
vv = state->wcur.ptr.p_double[i];
|
|
state->optstate.fi.ptr.p_double[i] = vv*(state->f-state->tasky.ptr.p_double[i]);
|
|
i = i+1;
|
|
goto lbl_26;
|
|
lbl_28:
|
|
goto lbl_22;
|
|
lbl_24:
|
|
if( !state->optstate.needf )
|
|
{
|
|
goto lbl_29;
|
|
}
|
|
|
|
/*
|
|
* calculate F = sum (wi*(f(xi,c)-yi))^2
|
|
*/
|
|
state->optstate.f = 0;
|
|
i = 0;
|
|
lbl_31:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_33;
|
|
}
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->optstate.x.ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needf = ae_true;
|
|
state->rstate.stage = 4;
|
|
goto lbl_rcomm;
|
|
lbl_4:
|
|
state->needf = ae_false;
|
|
vv = state->wcur.ptr.p_double[i];
|
|
state->optstate.f = state->optstate.f+ae_sqr(vv*(state->f-state->tasky.ptr.p_double[i]), _state);
|
|
i = i+1;
|
|
goto lbl_31;
|
|
lbl_33:
|
|
goto lbl_22;
|
|
lbl_29:
|
|
if( !state->optstate.needfg )
|
|
{
|
|
goto lbl_34;
|
|
}
|
|
|
|
/*
|
|
* calculate F/gradF
|
|
*/
|
|
state->optstate.f = 0;
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->optstate.g.ptr.p_double[i] = 0;
|
|
}
|
|
i = 0;
|
|
lbl_36:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_38;
|
|
}
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->optstate.x.ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needfg = ae_true;
|
|
state->rstate.stage = 5;
|
|
goto lbl_rcomm;
|
|
lbl_5:
|
|
state->needfg = ae_false;
|
|
vv = state->wcur.ptr.p_double[i];
|
|
state->optstate.f = state->optstate.f+ae_sqr(vv*(state->f-state->tasky.ptr.p_double[i]), _state);
|
|
v = ae_sqr(vv, _state)*2*(state->f-state->tasky.ptr.p_double[i]);
|
|
ae_v_addd(&state->optstate.g.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,k-1), v);
|
|
i = i+1;
|
|
goto lbl_36;
|
|
lbl_38:
|
|
goto lbl_22;
|
|
lbl_34:
|
|
if( !state->optstate.needfij )
|
|
{
|
|
goto lbl_39;
|
|
}
|
|
|
|
/*
|
|
* calculate Fi/jac(Fi)
|
|
*/
|
|
i = 0;
|
|
lbl_41:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_43;
|
|
}
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->optstate.x.ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needfg = ae_true;
|
|
state->rstate.stage = 6;
|
|
goto lbl_rcomm;
|
|
lbl_6:
|
|
state->needfg = ae_false;
|
|
vv = state->wcur.ptr.p_double[i];
|
|
state->optstate.fi.ptr.p_double[i] = vv*(state->f-state->tasky.ptr.p_double[i]);
|
|
ae_v_moved(&state->optstate.j.ptr.pp_double[i][0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,k-1), vv);
|
|
i = i+1;
|
|
goto lbl_41;
|
|
lbl_43:
|
|
goto lbl_22;
|
|
lbl_39:
|
|
if( !state->optstate.needfgh )
|
|
{
|
|
goto lbl_44;
|
|
}
|
|
|
|
/*
|
|
* calculate F/grad(F)/hess(F)
|
|
*/
|
|
state->optstate.f = 0;
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->optstate.g.ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
state->optstate.h.ptr.pp_double[i][j] = 0;
|
|
}
|
|
}
|
|
i = 0;
|
|
lbl_46:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_48;
|
|
}
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->optstate.x.ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needfgh = ae_true;
|
|
state->rstate.stage = 7;
|
|
goto lbl_rcomm;
|
|
lbl_7:
|
|
state->needfgh = ae_false;
|
|
vv = state->wcur.ptr.p_double[i];
|
|
state->optstate.f = state->optstate.f+ae_sqr(vv*(state->f-state->tasky.ptr.p_double[i]), _state);
|
|
v = ae_sqr(vv, _state)*2*(state->f-state->tasky.ptr.p_double[i]);
|
|
ae_v_addd(&state->optstate.g.ptr.p_double[0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,k-1), v);
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
v = 2*ae_sqr(vv, _state)*state->g.ptr.p_double[j];
|
|
ae_v_addd(&state->optstate.h.ptr.pp_double[j][0], 1, &state->g.ptr.p_double[0], 1, ae_v_len(0,k-1), v);
|
|
v = 2*ae_sqr(vv, _state)*(state->f-state->tasky.ptr.p_double[i]);
|
|
ae_v_addd(&state->optstate.h.ptr.pp_double[j][0], 1, &state->h.ptr.pp_double[j][0], 1, ae_v_len(0,k-1), v);
|
|
}
|
|
i = i+1;
|
|
goto lbl_46;
|
|
lbl_48:
|
|
goto lbl_22;
|
|
lbl_44:
|
|
if( !state->optstate.xupdated )
|
|
{
|
|
goto lbl_49;
|
|
}
|
|
|
|
/*
|
|
* Report new iteration
|
|
*/
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->optstate.x.ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
state->f = state->optstate.f;
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->xupdated = ae_true;
|
|
state->rstate.stage = 8;
|
|
goto lbl_rcomm;
|
|
lbl_8:
|
|
state->xupdated = ae_false;
|
|
goto lbl_22;
|
|
lbl_49:
|
|
goto lbl_22;
|
|
lbl_23:
|
|
minlmresults(&state->optstate, &state->c, &state->optrep, _state);
|
|
state->repterminationtype = state->optrep.terminationtype;
|
|
state->repiterationscount = state->optrep.iterationscount;
|
|
|
|
/*
|
|
* calculate errors
|
|
*/
|
|
if( state->repterminationtype<=0 )
|
|
{
|
|
goto lbl_51;
|
|
}
|
|
|
|
/*
|
|
* Calculate RMS/Avg/Max/... errors
|
|
*/
|
|
state->reprmserror = 0;
|
|
state->repwrmserror = 0;
|
|
state->repavgerror = 0;
|
|
state->repavgrelerror = 0;
|
|
state->repmaxerror = 0;
|
|
relcnt = 0;
|
|
i = 0;
|
|
lbl_53:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_55;
|
|
}
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needf = ae_true;
|
|
state->rstate.stage = 9;
|
|
goto lbl_rcomm;
|
|
lbl_9:
|
|
state->needf = ae_false;
|
|
v = state->f;
|
|
vv = state->wcur.ptr.p_double[i];
|
|
state->reprmserror = state->reprmserror+ae_sqr(v-state->tasky.ptr.p_double[i], _state);
|
|
state->repwrmserror = state->repwrmserror+ae_sqr(vv*(v-state->tasky.ptr.p_double[i]), _state);
|
|
state->repavgerror = state->repavgerror+ae_fabs(v-state->tasky.ptr.p_double[i], _state);
|
|
if( ae_fp_neq(state->tasky.ptr.p_double[i],0) )
|
|
{
|
|
state->repavgrelerror = state->repavgrelerror+ae_fabs(v-state->tasky.ptr.p_double[i], _state)/ae_fabs(state->tasky.ptr.p_double[i], _state);
|
|
relcnt = relcnt+1;
|
|
}
|
|
state->repmaxerror = ae_maxreal(state->repmaxerror, ae_fabs(v-state->tasky.ptr.p_double[i], _state), _state);
|
|
i = i+1;
|
|
goto lbl_53;
|
|
lbl_55:
|
|
state->reprmserror = ae_sqrt(state->reprmserror/n, _state);
|
|
state->repwrmserror = ae_sqrt(state->repwrmserror/n, _state);
|
|
state->repavgerror = state->repavgerror/n;
|
|
if( ae_fp_neq(relcnt,0) )
|
|
{
|
|
state->repavgrelerror = state->repavgrelerror/relcnt;
|
|
}
|
|
|
|
/*
|
|
* Calculate covariance matrix
|
|
*/
|
|
rmatrixsetlengthatleast(&state->tmpjac, n, k, _state);
|
|
rvectorsetlengthatleast(&state->tmpf, n, _state);
|
|
rvectorsetlengthatleast(&state->tmp, k, _state);
|
|
if( ae_fp_less_eq(state->diffstep,0) )
|
|
{
|
|
goto lbl_56;
|
|
}
|
|
|
|
/*
|
|
* Compute Jacobian by means of numerical differentiation
|
|
*/
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needf = ae_true;
|
|
i = 0;
|
|
lbl_58:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_60;
|
|
}
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
state->rstate.stage = 10;
|
|
goto lbl_rcomm;
|
|
lbl_10:
|
|
state->tmpf.ptr.p_double[i] = state->f;
|
|
j = 0;
|
|
lbl_61:
|
|
if( j>k-1 )
|
|
{
|
|
goto lbl_63;
|
|
}
|
|
v = state->c.ptr.p_double[j];
|
|
lx = v-state->diffstep*state->s.ptr.p_double[j];
|
|
state->c.ptr.p_double[j] = lx;
|
|
if( ae_isfinite(state->bndl.ptr.p_double[j], _state) )
|
|
{
|
|
state->c.ptr.p_double[j] = ae_maxreal(state->c.ptr.p_double[j], state->bndl.ptr.p_double[j], _state);
|
|
}
|
|
state->rstate.stage = 11;
|
|
goto lbl_rcomm;
|
|
lbl_11:
|
|
lf = state->f;
|
|
rx = v+state->diffstep*state->s.ptr.p_double[j];
|
|
state->c.ptr.p_double[j] = rx;
|
|
if( ae_isfinite(state->bndu.ptr.p_double[j], _state) )
|
|
{
|
|
state->c.ptr.p_double[j] = ae_minreal(state->c.ptr.p_double[j], state->bndu.ptr.p_double[j], _state);
|
|
}
|
|
state->rstate.stage = 12;
|
|
goto lbl_rcomm;
|
|
lbl_12:
|
|
rf = state->f;
|
|
state->c.ptr.p_double[j] = v;
|
|
if( ae_fp_neq(rx,lx) )
|
|
{
|
|
state->tmpjac.ptr.pp_double[i][j] = (rf-lf)/(rx-lx);
|
|
}
|
|
else
|
|
{
|
|
state->tmpjac.ptr.pp_double[i][j] = 0;
|
|
}
|
|
j = j+1;
|
|
goto lbl_61;
|
|
lbl_63:
|
|
i = i+1;
|
|
goto lbl_58;
|
|
lbl_60:
|
|
state->needf = ae_false;
|
|
goto lbl_57;
|
|
lbl_56:
|
|
|
|
/*
|
|
* Jacobian is calculated with user-provided analytic gradient
|
|
*/
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
state->needfg = ae_true;
|
|
i = 0;
|
|
lbl_64:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_66;
|
|
}
|
|
ae_v_move(&state->x.ptr.p_double[0], 1, &state->taskx.ptr.pp_double[i][0], 1, ae_v_len(0,m-1));
|
|
state->pointindex = i;
|
|
state->rstate.stage = 13;
|
|
goto lbl_rcomm;
|
|
lbl_13:
|
|
state->tmpf.ptr.p_double[i] = state->f;
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
state->tmpjac.ptr.pp_double[i][j] = state->g.ptr.p_double[j];
|
|
}
|
|
i = i+1;
|
|
goto lbl_64;
|
|
lbl_66:
|
|
state->needfg = ae_false;
|
|
lbl_57:
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->tmp.ptr.p_double[i] = 0.0;
|
|
}
|
|
lsfit_estimateerrors(&state->tmpjac, &state->tmpf, &state->tasky, &state->wcur, &state->tmp, &state->s, n, k, &state->rep, &state->tmpjacw, 0, _state);
|
|
lbl_51:
|
|
result = ae_false;
|
|
return result;
|
|
|
|
/*
|
|
* Saving state
|
|
*/
|
|
lbl_rcomm:
|
|
result = ae_true;
|
|
state->rstate.ia.ptr.p_int[0] = n;
|
|
state->rstate.ia.ptr.p_int[1] = m;
|
|
state->rstate.ia.ptr.p_int[2] = k;
|
|
state->rstate.ia.ptr.p_int[3] = i;
|
|
state->rstate.ia.ptr.p_int[4] = j;
|
|
state->rstate.ia.ptr.p_int[5] = j1;
|
|
state->rstate.ia.ptr.p_int[6] = info;
|
|
state->rstate.ra.ptr.p_double[0] = lx;
|
|
state->rstate.ra.ptr.p_double[1] = lf;
|
|
state->rstate.ra.ptr.p_double[2] = ld;
|
|
state->rstate.ra.ptr.p_double[3] = rx;
|
|
state->rstate.ra.ptr.p_double[4] = rf;
|
|
state->rstate.ra.ptr.p_double[5] = rd;
|
|
state->rstate.ra.ptr.p_double[6] = v;
|
|
state->rstate.ra.ptr.p_double[7] = vv;
|
|
state->rstate.ra.ptr.p_double[8] = relcnt;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting results.
|
|
|
|
Called after return from LSFitFit().
|
|
|
|
INPUT PARAMETERS:
|
|
State - algorithm state
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - completion code:
|
|
* -7 gradient verification failed.
|
|
See LSFitSetGradientCheck() for more information.
|
|
* 1 relative function improvement is no more than
|
|
EpsF.
|
|
* 2 relative step is no more than EpsX.
|
|
* 4 gradient norm is no more than EpsG
|
|
* 5 MaxIts steps was taken
|
|
* 7 stopping conditions are too stringent,
|
|
further improvement is impossible
|
|
C - array[0..K-1], solution
|
|
Rep - optimization report. On success following fields are set:
|
|
* R2 non-adjusted coefficient of determination
|
|
(non-weighted)
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
* WRMSError weighted rms error on the (X,Y).
|
|
|
|
ERRORS IN PARAMETERS
|
|
|
|
This solver also calculates different kinds of errors in parameters and
|
|
fills corresponding fields of report:
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(J*CovPar*J')),
|
|
where J is Jacobian matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
NOTE: covariance matrix is estimated using correction for degrees
|
|
of freedom (covariances are divided by N-M instead of dividing
|
|
by N).
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitresults(lsfitstate* state,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
*info = 0;
|
|
ae_vector_clear(c);
|
|
_lsfitreport_clear(rep);
|
|
|
|
lsfit_clearreport(rep, _state);
|
|
*info = state->repterminationtype;
|
|
rep->varidx = state->repvaridx;
|
|
if( *info>0 )
|
|
{
|
|
ae_vector_set_length(c, state->k, _state);
|
|
ae_v_move(&c->ptr.p_double[0], 1, &state->c.ptr.p_double[0], 1, ae_v_len(0,state->k-1));
|
|
rep->rmserror = state->reprmserror;
|
|
rep->wrmserror = state->repwrmserror;
|
|
rep->avgerror = state->repavgerror;
|
|
rep->avgrelerror = state->repavgrelerror;
|
|
rep->maxerror = state->repmaxerror;
|
|
rep->iterationscount = state->repiterationscount;
|
|
ae_matrix_set_length(&rep->covpar, state->k, state->k, _state);
|
|
ae_vector_set_length(&rep->errpar, state->k, _state);
|
|
ae_vector_set_length(&rep->errcurve, state->npoints, _state);
|
|
ae_vector_set_length(&rep->noise, state->npoints, _state);
|
|
rep->r2 = state->rep.r2;
|
|
for(i=0; i<=state->k-1; i++)
|
|
{
|
|
for(j=0; j<=state->k-1; j++)
|
|
{
|
|
rep->covpar.ptr.pp_double[i][j] = state->rep.covpar.ptr.pp_double[i][j];
|
|
}
|
|
rep->errpar.ptr.p_double[i] = state->rep.errpar.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=state->npoints-1; i++)
|
|
{
|
|
rep->errcurve.ptr.p_double[i] = state->rep.errcurve.ptr.p_double[i];
|
|
rep->noise.ptr.p_double[i] = state->rep.noise.ptr.p_double[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine turns on verification of the user-supplied analytic
|
|
gradient:
|
|
* user calls this subroutine before fitting begins
|
|
* LSFitFit() is called
|
|
* prior to actual fitting, for each point in data set X_i and each
|
|
component of parameters being fited C_j algorithm performs following
|
|
steps:
|
|
* two trial steps are made to C_j-TestStep*S[j] and C_j+TestStep*S[j],
|
|
where C_j is j-th parameter and S[j] is a scale of j-th parameter
|
|
* if needed, steps are bounded with respect to constraints on C[]
|
|
* F(X_i|C) is evaluated at these trial points
|
|
* we perform one more evaluation in the middle point of the interval
|
|
* we build cubic model using function values and derivatives at trial
|
|
points and we compare its prediction with actual value in the middle
|
|
point
|
|
* in case difference between prediction and actual value is higher than
|
|
some predetermined threshold, algorithm stops with completion code -7;
|
|
Rep.VarIdx is set to index of the parameter with incorrect derivative.
|
|
* after verification is over, algorithm proceeds to the actual optimization.
|
|
|
|
NOTE 1: verification needs N*K (points count * parameters count) gradient
|
|
evaluations. It is very costly and you should use it only for low
|
|
dimensional problems, when you want to be sure that you've
|
|
correctly calculated analytic derivatives. You should not use it
|
|
in the production code (unless you want to check derivatives
|
|
provided by some third party).
|
|
|
|
NOTE 2: you should carefully choose TestStep. Value which is too large
|
|
(so large that function behaviour is significantly non-cubic) will
|
|
lead to false alarms. You may use different step for different
|
|
parameters by means of setting scale with LSFitSetScale().
|
|
|
|
NOTE 3: this function may lead to false positives. In case it reports that
|
|
I-th derivative was calculated incorrectly, you may decrease test
|
|
step and try one more time - maybe your function changes too
|
|
sharply and your step is too large for such rapidly chanding
|
|
function.
|
|
|
|
NOTE 4: this function works only for optimizers created with LSFitCreateWFG()
|
|
or LSFitCreateFG() constructors.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure used to store algorithm state
|
|
TestStep - verification step:
|
|
* TestStep=0 turns verification off
|
|
* TestStep>0 activates verification
|
|
|
|
-- ALGLIB --
|
|
Copyright 15.06.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetgradientcheck(lsfitstate* state,
|
|
double teststep,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(teststep, _state), "LSFitSetGradientCheck: TestStep contains NaN or Infinite", _state);
|
|
ae_assert(ae_fp_greater_eq(teststep,0), "LSFitSetGradientCheck: invalid argument TestStep(TestStep<0)", _state);
|
|
state->teststep = teststep;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine: automatic scaling for LLS tasks.
|
|
NEVER CALL IT DIRECTLY!
|
|
|
|
Maps abscissas to [-1,1], standartizes ordinates and correspondingly scales
|
|
constraints. It also scales weights so that max(W[i])=1
|
|
|
|
Transformations performed:
|
|
* X, XC [XA,XB] => [-1,+1]
|
|
transformation makes min(X)=-1, max(X)=+1
|
|
|
|
* Y [SA,SB] => [0,1]
|
|
transformation makes mean(Y)=0, stddev(Y)=1
|
|
|
|
* YC transformed accordingly to SA, SB, DC[I]
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 08.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitscalexy(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
double* xa,
|
|
double* xb,
|
|
double* sa,
|
|
double* sb,
|
|
/* Real */ ae_vector* xoriginal,
|
|
/* Real */ ae_vector* yoriginal,
|
|
ae_state *_state)
|
|
{
|
|
double xmin;
|
|
double xmax;
|
|
ae_int_t i;
|
|
double mx;
|
|
|
|
*xa = 0;
|
|
*xb = 0;
|
|
*sa = 0;
|
|
*sb = 0;
|
|
ae_vector_clear(xoriginal);
|
|
ae_vector_clear(yoriginal);
|
|
|
|
ae_assert(n>=1, "LSFitScaleXY: incorrect N", _state);
|
|
ae_assert(k>=0, "LSFitScaleXY: incorrect K", _state);
|
|
|
|
/*
|
|
* Calculate xmin/xmax.
|
|
* Force xmin<>xmax.
|
|
*/
|
|
xmin = x->ptr.p_double[0];
|
|
xmax = x->ptr.p_double[0];
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
xmin = ae_minreal(xmin, x->ptr.p_double[i], _state);
|
|
xmax = ae_maxreal(xmax, x->ptr.p_double[i], _state);
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
xmin = ae_minreal(xmin, xc->ptr.p_double[i], _state);
|
|
xmax = ae_maxreal(xmax, xc->ptr.p_double[i], _state);
|
|
}
|
|
if( ae_fp_eq(xmin,xmax) )
|
|
{
|
|
if( ae_fp_eq(xmin,0) )
|
|
{
|
|
xmin = -1;
|
|
xmax = 1;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater(xmin,0) )
|
|
{
|
|
xmin = 0.5*xmin;
|
|
}
|
|
else
|
|
{
|
|
xmax = 0.5*xmax;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Transform abscissas: map [XA,XB] to [0,1]
|
|
*
|
|
* Store old X[] in XOriginal[] (it will be used
|
|
* to calculate relative error).
|
|
*/
|
|
ae_vector_set_length(xoriginal, n, _state);
|
|
ae_v_move(&xoriginal->ptr.p_double[0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
*xa = xmin;
|
|
*xb = xmax;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
x->ptr.p_double[i] = 2*(x->ptr.p_double[i]-0.5*(*xa+(*xb)))/(*xb-(*xa));
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(dc->ptr.p_int[i]>=0, "LSFitScaleXY: internal error!", _state);
|
|
xc->ptr.p_double[i] = 2*(xc->ptr.p_double[i]-0.5*(*xa+(*xb)))/(*xb-(*xa));
|
|
yc->ptr.p_double[i] = yc->ptr.p_double[i]*ae_pow(0.5*(*xb-(*xa)), dc->ptr.p_int[i], _state);
|
|
}
|
|
|
|
/*
|
|
* Transform function values: map [SA,SB] to [0,1]
|
|
* SA = mean(Y),
|
|
* SB = SA+stddev(Y).
|
|
*
|
|
* Store old Y[] in YOriginal[] (it will be used
|
|
* to calculate relative error).
|
|
*/
|
|
ae_vector_set_length(yoriginal, n, _state);
|
|
ae_v_move(&yoriginal->ptr.p_double[0], 1, &y->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
*sa = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
*sa = *sa+y->ptr.p_double[i];
|
|
}
|
|
*sa = *sa/n;
|
|
*sb = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
*sb = *sb+ae_sqr(y->ptr.p_double[i]-(*sa), _state);
|
|
}
|
|
*sb = ae_sqrt(*sb/n, _state)+(*sa);
|
|
if( ae_fp_eq(*sb,*sa) )
|
|
{
|
|
*sb = 2*(*sa);
|
|
}
|
|
if( ae_fp_eq(*sb,*sa) )
|
|
{
|
|
*sb = *sa+1;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
y->ptr.p_double[i] = (y->ptr.p_double[i]-(*sa))/(*sb-(*sa));
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
if( dc->ptr.p_int[i]==0 )
|
|
{
|
|
yc->ptr.p_double[i] = (yc->ptr.p_double[i]-(*sa))/(*sb-(*sa));
|
|
}
|
|
else
|
|
{
|
|
yc->ptr.p_double[i] = yc->ptr.p_double[i]/(*sb-(*sa));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Scale weights
|
|
*/
|
|
mx = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
mx = ae_maxreal(mx, ae_fabs(w->ptr.p_double[i], _state), _state);
|
|
}
|
|
if( ae_fp_neq(mx,0) )
|
|
{
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w->ptr.p_double[i] = w->ptr.p_double[i]/mx;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal spline fitting subroutine
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 08.09.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void lsfit_spline1dfitinternal(ae_int_t st,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _w;
|
|
ae_vector _xc;
|
|
ae_vector _yc;
|
|
ae_matrix fmatrix;
|
|
ae_matrix cmatrix;
|
|
ae_vector y2;
|
|
ae_vector w2;
|
|
ae_vector sx;
|
|
ae_vector sy;
|
|
ae_vector sd;
|
|
ae_vector tmp;
|
|
ae_vector xoriginal;
|
|
ae_vector yoriginal;
|
|
lsfitreport lrep;
|
|
double v0;
|
|
double v1;
|
|
double v2;
|
|
double mx;
|
|
spline1dinterpolant s2;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t relcnt;
|
|
double xa;
|
|
double xb;
|
|
double sa;
|
|
double sb;
|
|
double bl;
|
|
double br;
|
|
double decay;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_w, w, _state, ae_true);
|
|
w = &_w;
|
|
ae_vector_init_copy(&_xc, xc, _state, ae_true);
|
|
xc = &_xc;
|
|
ae_vector_init_copy(&_yc, yc, _state, ae_true);
|
|
yc = &_yc;
|
|
*info = 0;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
ae_matrix_init(&fmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&cmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sy, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sd, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xoriginal, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yoriginal, 0, DT_REAL, _state, ae_true);
|
|
_lsfitreport_init(&lrep, _state, ae_true);
|
|
_spline1dinterpolant_init(&s2, _state, ae_true);
|
|
|
|
ae_assert(st==0||st==1, "Spline1DFit: internal error!", _state);
|
|
if( st==0&&m<4 )
|
|
{
|
|
*info = -1;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( st==1&&m<4 )
|
|
{
|
|
*info = -1;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( (n<1||k<0)||k>=m )
|
|
{
|
|
*info = -1;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
*info = 0;
|
|
if( dc->ptr.p_int[i]<0 )
|
|
{
|
|
*info = -1;
|
|
}
|
|
if( dc->ptr.p_int[i]>1 )
|
|
{
|
|
*info = -1;
|
|
}
|
|
if( *info<0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
if( st==1&&m%2!=0 )
|
|
{
|
|
|
|
/*
|
|
* Hermite fitter must have even number of basis functions
|
|
*/
|
|
*info = -2;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* weight decay for correct handling of task which becomes
|
|
* degenerate after constraints are applied
|
|
*/
|
|
decay = 10000*ae_machineepsilon;
|
|
|
|
/*
|
|
* Scale X, Y, XC, YC
|
|
*/
|
|
lsfitscalexy(x, y, w, n, xc, yc, dc, k, &xa, &xb, &sa, &sb, &xoriginal, &yoriginal, _state);
|
|
|
|
/*
|
|
* allocate space, initialize:
|
|
* * SX - grid for basis functions
|
|
* * SY - values of basis functions at grid points
|
|
* * FMatrix- values of basis functions at X[]
|
|
* * CMatrix- values (derivatives) of basis functions at XC[]
|
|
*/
|
|
ae_vector_set_length(&y2, n+m, _state);
|
|
ae_vector_set_length(&w2, n+m, _state);
|
|
ae_matrix_set_length(&fmatrix, n+m, m, _state);
|
|
if( k>0 )
|
|
{
|
|
ae_matrix_set_length(&cmatrix, k, m+1, _state);
|
|
}
|
|
if( st==0 )
|
|
{
|
|
|
|
/*
|
|
* allocate space for cubic spline
|
|
*/
|
|
ae_vector_set_length(&sx, m-2, _state);
|
|
ae_vector_set_length(&sy, m-2, _state);
|
|
for(j=0; j<=m-2-1; j++)
|
|
{
|
|
sx.ptr.p_double[j] = (double)(2*j)/(double)(m-2-1)-1;
|
|
}
|
|
}
|
|
if( st==1 )
|
|
{
|
|
|
|
/*
|
|
* allocate space for Hermite spline
|
|
*/
|
|
ae_vector_set_length(&sx, m/2, _state);
|
|
ae_vector_set_length(&sy, m/2, _state);
|
|
ae_vector_set_length(&sd, m/2, _state);
|
|
for(j=0; j<=m/2-1; j++)
|
|
{
|
|
sx.ptr.p_double[j] = (double)(2*j)/(double)(m/2-1)-1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Prepare design and constraints matrices:
|
|
* * fill constraints matrix
|
|
* * fill first N rows of design matrix with values
|
|
* * fill next M rows of design matrix with regularizing term
|
|
* * append M zeros to Y
|
|
* * append M elements, mean(abs(W)) each, to W
|
|
*/
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
|
|
/*
|
|
* prepare Jth basis function
|
|
*/
|
|
if( st==0 )
|
|
{
|
|
|
|
/*
|
|
* cubic spline basis
|
|
*/
|
|
for(i=0; i<=m-2-1; i++)
|
|
{
|
|
sy.ptr.p_double[i] = 0;
|
|
}
|
|
bl = 0;
|
|
br = 0;
|
|
if( j<m-2 )
|
|
{
|
|
sy.ptr.p_double[j] = 1;
|
|
}
|
|
if( j==m-2 )
|
|
{
|
|
bl = 1;
|
|
}
|
|
if( j==m-1 )
|
|
{
|
|
br = 1;
|
|
}
|
|
spline1dbuildcubic(&sx, &sy, m-2, 1, bl, 1, br, &s2, _state);
|
|
}
|
|
if( st==1 )
|
|
{
|
|
|
|
/*
|
|
* Hermite basis
|
|
*/
|
|
for(i=0; i<=m/2-1; i++)
|
|
{
|
|
sy.ptr.p_double[i] = 0;
|
|
sd.ptr.p_double[i] = 0;
|
|
}
|
|
if( j%2==0 )
|
|
{
|
|
sy.ptr.p_double[j/2] = 1;
|
|
}
|
|
else
|
|
{
|
|
sd.ptr.p_double[j/2] = 1;
|
|
}
|
|
spline1dbuildhermite(&sx, &sy, &sd, m/2, &s2, _state);
|
|
}
|
|
|
|
/*
|
|
* values at X[], XC[]
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
fmatrix.ptr.pp_double[i][j] = spline1dcalc(&s2, x->ptr.p_double[i], _state);
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(dc->ptr.p_int[i]>=0&&dc->ptr.p_int[i]<=2, "Spline1DFit: internal error!", _state);
|
|
spline1ddiff(&s2, xc->ptr.p_double[i], &v0, &v1, &v2, _state);
|
|
if( dc->ptr.p_int[i]==0 )
|
|
{
|
|
cmatrix.ptr.pp_double[i][j] = v0;
|
|
}
|
|
if( dc->ptr.p_int[i]==1 )
|
|
{
|
|
cmatrix.ptr.pp_double[i][j] = v1;
|
|
}
|
|
if( dc->ptr.p_int[i]==2 )
|
|
{
|
|
cmatrix.ptr.pp_double[i][j] = v2;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
cmatrix.ptr.pp_double[i][m] = yc->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
if( i==j )
|
|
{
|
|
fmatrix.ptr.pp_double[n+i][j] = decay;
|
|
}
|
|
else
|
|
{
|
|
fmatrix.ptr.pp_double[n+i][j] = 0;
|
|
}
|
|
}
|
|
}
|
|
ae_vector_set_length(&y2, n+m, _state);
|
|
ae_vector_set_length(&w2, n+m, _state);
|
|
ae_v_move(&y2.ptr.p_double[0], 1, &y->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_v_move(&w2.ptr.p_double[0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
mx = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
mx = mx+ae_fabs(w->ptr.p_double[i], _state);
|
|
}
|
|
mx = mx/n;
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
y2.ptr.p_double[n+i] = 0;
|
|
w2.ptr.p_double[n+i] = mx;
|
|
}
|
|
|
|
/*
|
|
* Solve constrained task
|
|
*/
|
|
if( k>0 )
|
|
{
|
|
|
|
/*
|
|
* solve using regularization
|
|
*/
|
|
lsfitlinearwc(&y2, &w2, &fmatrix, &cmatrix, n+m, m, k, info, &tmp, &lrep, _state);
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* no constraints, no regularization needed
|
|
*/
|
|
lsfitlinearwc(y, w, &fmatrix, &cmatrix, n, m, k, info, &tmp, &lrep, _state);
|
|
}
|
|
if( *info<0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Generate spline and scale it
|
|
*/
|
|
if( st==0 )
|
|
{
|
|
|
|
/*
|
|
* cubic spline basis
|
|
*/
|
|
ae_v_move(&sy.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,m-2-1));
|
|
spline1dbuildcubic(&sx, &sy, m-2, 1, tmp.ptr.p_double[m-2], 1, tmp.ptr.p_double[m-1], s, _state);
|
|
}
|
|
if( st==1 )
|
|
{
|
|
|
|
/*
|
|
* Hermite basis
|
|
*/
|
|
for(i=0; i<=m/2-1; i++)
|
|
{
|
|
sy.ptr.p_double[i] = tmp.ptr.p_double[2*i];
|
|
sd.ptr.p_double[i] = tmp.ptr.p_double[2*i+1];
|
|
}
|
|
spline1dbuildhermite(&sx, &sy, &sd, m/2, s, _state);
|
|
}
|
|
spline1dlintransx(s, 2/(xb-xa), -(xa+xb)/(xb-xa), _state);
|
|
spline1dlintransy(s, sb-sa, sa, _state);
|
|
|
|
/*
|
|
* Scale absolute errors obtained from LSFitLinearW.
|
|
* Relative error should be calculated separately
|
|
* (because of shifting/scaling of the task)
|
|
*/
|
|
rep->taskrcond = lrep.taskrcond;
|
|
rep->rmserror = lrep.rmserror*(sb-sa);
|
|
rep->avgerror = lrep.avgerror*(sb-sa);
|
|
rep->maxerror = lrep.maxerror*(sb-sa);
|
|
rep->avgrelerror = 0;
|
|
relcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(yoriginal.ptr.p_double[i],0) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror+ae_fabs(spline1dcalc(s, xoriginal.ptr.p_double[i], _state)-yoriginal.ptr.p_double[i], _state)/ae_fabs(yoriginal.ptr.p_double[i], _state);
|
|
relcnt = relcnt+1;
|
|
}
|
|
}
|
|
if( relcnt!=0 )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror/relcnt;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal fitting subroutine
|
|
*************************************************************************/
|
|
static void lsfit_lsfitlinearinternal(/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_matrix* fmatrix,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
double threshold;
|
|
ae_matrix ft;
|
|
ae_matrix q;
|
|
ae_matrix l;
|
|
ae_matrix r;
|
|
ae_vector b;
|
|
ae_vector wmod;
|
|
ae_vector tau;
|
|
ae_vector nzeros;
|
|
ae_vector s;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double v;
|
|
ae_vector sv;
|
|
ae_matrix u;
|
|
ae_matrix vt;
|
|
ae_vector tmp;
|
|
ae_vector utb;
|
|
ae_vector sutb;
|
|
ae_int_t relcnt;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*info = 0;
|
|
ae_vector_clear(c);
|
|
_lsfitreport_clear(rep);
|
|
ae_matrix_init(&ft, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&q, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&l, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&r, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&b, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&wmod, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tau, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&nzeros, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sv, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&u, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&vt, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&utb, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sutb, 0, DT_REAL, _state, ae_true);
|
|
|
|
lsfit_clearreport(rep, _state);
|
|
if( n<1||m<1 )
|
|
{
|
|
*info = -1;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
*info = 1;
|
|
threshold = ae_sqrt(ae_machineepsilon, _state);
|
|
|
|
/*
|
|
* Degenerate case, needs special handling
|
|
*/
|
|
if( n<m )
|
|
{
|
|
|
|
/*
|
|
* Create design matrix.
|
|
*/
|
|
ae_matrix_set_length(&ft, n, m, _state);
|
|
ae_vector_set_length(&b, n, _state);
|
|
ae_vector_set_length(&wmod, n, _state);
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
v = w->ptr.p_double[j];
|
|
ae_v_moved(&ft.ptr.pp_double[j][0], 1, &fmatrix->ptr.pp_double[j][0], 1, ae_v_len(0,m-1), v);
|
|
b.ptr.p_double[j] = w->ptr.p_double[j]*y->ptr.p_double[j];
|
|
wmod.ptr.p_double[j] = 1;
|
|
}
|
|
|
|
/*
|
|
* LQ decomposition and reduction to M=N
|
|
*/
|
|
ae_vector_set_length(c, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
c->ptr.p_double[i] = 0;
|
|
}
|
|
rep->taskrcond = 0;
|
|
rmatrixlq(&ft, n, m, &tau, _state);
|
|
rmatrixlqunpackq(&ft, n, m, &tau, n, &q, _state);
|
|
rmatrixlqunpackl(&ft, n, m, &l, _state);
|
|
lsfit_lsfitlinearinternal(&b, &wmod, &l, n, n, info, &tmp, rep, _state);
|
|
if( *info<=0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = tmp.ptr.p_double[i];
|
|
ae_v_addd(&c->ptr.p_double[0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* N>=M. Generate design matrix and reduce to N=M using
|
|
* QR decomposition.
|
|
*/
|
|
ae_matrix_set_length(&ft, n, m, _state);
|
|
ae_vector_set_length(&b, n, _state);
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
v = w->ptr.p_double[j];
|
|
ae_v_moved(&ft.ptr.pp_double[j][0], 1, &fmatrix->ptr.pp_double[j][0], 1, ae_v_len(0,m-1), v);
|
|
b.ptr.p_double[j] = w->ptr.p_double[j]*y->ptr.p_double[j];
|
|
}
|
|
rmatrixqr(&ft, n, m, &tau, _state);
|
|
rmatrixqrunpackq(&ft, n, m, &tau, m, &q, _state);
|
|
rmatrixqrunpackr(&ft, n, m, &r, _state);
|
|
ae_vector_set_length(&tmp, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
tmp.ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = b.ptr.p_double[i];
|
|
ae_v_addd(&tmp.ptr.p_double[0], 1, &q.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
|
|
}
|
|
ae_vector_set_length(&b, m, _state);
|
|
ae_v_move(&b.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
|
|
/*
|
|
* R contains reduced MxM design upper triangular matrix,
|
|
* B contains reduced Mx1 right part.
|
|
*
|
|
* Determine system condition number and decide
|
|
* should we use triangular solver (faster) or
|
|
* SVD-based solver (more stable).
|
|
*
|
|
* We can use LU-based RCond estimator for this task.
|
|
*/
|
|
rep->taskrcond = rmatrixlurcondinf(&r, m, _state);
|
|
if( ae_fp_greater(rep->taskrcond,threshold) )
|
|
{
|
|
|
|
/*
|
|
* use QR-based solver
|
|
*/
|
|
ae_vector_set_length(c, m, _state);
|
|
c->ptr.p_double[m-1] = b.ptr.p_double[m-1]/r.ptr.pp_double[m-1][m-1];
|
|
for(i=m-2; i>=0; i--)
|
|
{
|
|
v = ae_v_dotproduct(&r.ptr.pp_double[i][i+1], 1, &c->ptr.p_double[i+1], 1, ae_v_len(i+1,m-1));
|
|
c->ptr.p_double[i] = (b.ptr.p_double[i]-v)/r.ptr.pp_double[i][i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* use SVD-based solver
|
|
*/
|
|
if( !rmatrixsvd(&r, m, m, 1, 1, 2, &sv, &u, &vt, _state) )
|
|
{
|
|
*info = -4;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_vector_set_length(&utb, m, _state);
|
|
ae_vector_set_length(&sutb, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
utb.ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
v = b.ptr.p_double[i];
|
|
ae_v_addd(&utb.ptr.p_double[0], 1, &u.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
|
|
}
|
|
if( ae_fp_greater(sv.ptr.p_double[0],0) )
|
|
{
|
|
rep->taskrcond = sv.ptr.p_double[m-1]/sv.ptr.p_double[0];
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
if( ae_fp_greater(sv.ptr.p_double[i],threshold*sv.ptr.p_double[0]) )
|
|
{
|
|
sutb.ptr.p_double[i] = utb.ptr.p_double[i]/sv.ptr.p_double[i];
|
|
}
|
|
else
|
|
{
|
|
sutb.ptr.p_double[i] = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rep->taskrcond = 0;
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sutb.ptr.p_double[i] = 0;
|
|
}
|
|
}
|
|
ae_vector_set_length(c, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
c->ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
v = sutb.ptr.p_double[i];
|
|
ae_v_addd(&c->ptr.p_double[0], 1, &vt.ptr.pp_double[i][0], 1, ae_v_len(0,m-1), v);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* calculate errors
|
|
*/
|
|
rep->rmserror = 0;
|
|
rep->avgerror = 0;
|
|
rep->avgrelerror = 0;
|
|
rep->maxerror = 0;
|
|
relcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = ae_v_dotproduct(&fmatrix->ptr.pp_double[i][0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
rep->rmserror = rep->rmserror+ae_sqr(v-y->ptr.p_double[i], _state);
|
|
rep->avgerror = rep->avgerror+ae_fabs(v-y->ptr.p_double[i], _state);
|
|
if( ae_fp_neq(y->ptr.p_double[i],0) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror+ae_fabs(v-y->ptr.p_double[i], _state)/ae_fabs(y->ptr.p_double[i], _state);
|
|
relcnt = relcnt+1;
|
|
}
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v-y->ptr.p_double[i], _state), _state);
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/n, _state);
|
|
rep->avgerror = rep->avgerror/n;
|
|
if( relcnt!=0 )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror/relcnt;
|
|
}
|
|
ae_vector_set_length(&nzeros, n, _state);
|
|
ae_vector_set_length(&s, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
s.ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
s.ptr.p_double[j] = s.ptr.p_double[j]+ae_sqr(fmatrix->ptr.pp_double[i][j], _state);
|
|
}
|
|
nzeros.ptr.p_double[i] = 0;
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
if( ae_fp_neq(s.ptr.p_double[i],0) )
|
|
{
|
|
s.ptr.p_double[i] = ae_sqrt(1/s.ptr.p_double[i], _state);
|
|
}
|
|
else
|
|
{
|
|
s.ptr.p_double[i] = 1;
|
|
}
|
|
}
|
|
lsfit_estimateerrors(fmatrix, &nzeros, y, w, c, &s, n, m, rep, &r, 1, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine
|
|
*************************************************************************/
|
|
static void lsfit_lsfitclearrequestfields(lsfitstate* state,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
state->needf = ae_false;
|
|
state->needfg = ae_false;
|
|
state->needfgh = ae_false;
|
|
state->xupdated = ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine, calculates barycentric basis functions.
|
|
Used for efficient simultaneous calculation of N basis functions.
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void lsfit_barycentriccalcbasis(barycentricinterpolant* b,
|
|
double t,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
double s2;
|
|
double s;
|
|
double v;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
|
|
|
|
/*
|
|
* special case: N=1
|
|
*/
|
|
if( b->n==1 )
|
|
{
|
|
y->ptr.p_double[0] = 1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Here we assume that task is normalized, i.e.:
|
|
* 1. abs(Y[i])<=1
|
|
* 2. abs(W[i])<=1
|
|
* 3. X[] is ordered
|
|
*
|
|
* First, we decide: should we use "safe" formula (guarded
|
|
* against overflow) or fast one?
|
|
*/
|
|
s = ae_fabs(t-b->x.ptr.p_double[0], _state);
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
v = b->x.ptr.p_double[i];
|
|
if( ae_fp_eq(v,t) )
|
|
{
|
|
for(j=0; j<=b->n-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = 0;
|
|
}
|
|
y->ptr.p_double[i] = 1;
|
|
return;
|
|
}
|
|
v = ae_fabs(t-v, _state);
|
|
if( ae_fp_less(v,s) )
|
|
{
|
|
s = v;
|
|
}
|
|
}
|
|
s2 = 0;
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
v = s/(t-b->x.ptr.p_double[i]);
|
|
v = v*b->w.ptr.p_double[i];
|
|
y->ptr.p_double[i] = v;
|
|
s2 = s2+v;
|
|
}
|
|
v = 1/s2;
|
|
ae_v_muld(&y->ptr.p_double[0], 1, ae_v_len(0,b->n-1), v);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This is internal function for Chebyshev fitting.
|
|
|
|
It assumes that input data are normalized:
|
|
* X/XC belong to [-1,+1],
|
|
* mean(Y)=0, stddev(Y)=1.
|
|
|
|
It does not checks inputs for errors.
|
|
|
|
This function is used to fit general (shifted) Chebyshev models, power
|
|
basis models or barycentric models.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
W - weights, array[0..N-1]
|
|
N - number of points, N>0.
|
|
XC - points where polynomial values/derivatives are constrained,
|
|
array[0..K-1].
|
|
YC - values of constraints, array[0..K-1]
|
|
DC - array[0..K-1], types of constraints:
|
|
* DC[i]=0 means that P(XC[i])=YC[i]
|
|
* DC[i]=1 means that P'(XC[i])=YC[i]
|
|
K - number of constraints, 0<=K<M.
|
|
K=0 means no constraints (XC/YC/DC are not used in such cases)
|
|
M - number of basis functions (= polynomial_degree + 1), M>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info- same format as in LSFitLinearW() subroutine:
|
|
* Info>0 task is solved
|
|
* Info<=0 an error occured:
|
|
-4 means inconvergence of internal SVD
|
|
-3 means inconsistent constraints
|
|
C - interpolant in Chebyshev form; [-1,+1] is used as base interval
|
|
Rep - report, same format as in LSFitLinearW() subroutine.
|
|
Following fields are set:
|
|
* RMSError rms error on the (X,Y).
|
|
* AvgError average error on the (X,Y).
|
|
* AvgRelError average relative error on the non-zero Y
|
|
* MaxError maximum error
|
|
NON-WEIGHTED ERRORS ARE CALCULATED
|
|
|
|
IMPORTANT:
|
|
this subroitine doesn't calculate task's condition number for K<>0.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void lsfit_internalchebyshevfit(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t* info,
|
|
/* Real */ ae_vector* c,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _xc;
|
|
ae_vector _yc;
|
|
ae_vector y2;
|
|
ae_vector w2;
|
|
ae_vector tmp;
|
|
ae_vector tmp2;
|
|
ae_vector tmpdiff;
|
|
ae_vector bx;
|
|
ae_vector by;
|
|
ae_vector bw;
|
|
ae_matrix fmatrix;
|
|
ae_matrix cmatrix;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double mx;
|
|
double decay;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_xc, xc, _state, ae_true);
|
|
xc = &_xc;
|
|
ae_vector_init_copy(&_yc, yc, _state, ae_true);
|
|
yc = &_yc;
|
|
*info = 0;
|
|
ae_vector_clear(c);
|
|
_lsfitreport_clear(rep);
|
|
ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpdiff, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&by, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bw, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&fmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&cmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
|
|
lsfit_clearreport(rep, _state);
|
|
|
|
/*
|
|
* weight decay for correct handling of task which becomes
|
|
* degenerate after constraints are applied
|
|
*/
|
|
decay = 10000*ae_machineepsilon;
|
|
|
|
/*
|
|
* allocate space, initialize/fill:
|
|
* * FMatrix- values of basis functions at X[]
|
|
* * CMatrix- values (derivatives) of basis functions at XC[]
|
|
* * fill constraints matrix
|
|
* * fill first N rows of design matrix with values
|
|
* * fill next M rows of design matrix with regularizing term
|
|
* * append M zeros to Y
|
|
* * append M elements, mean(abs(W)) each, to W
|
|
*/
|
|
ae_vector_set_length(&y2, n+m, _state);
|
|
ae_vector_set_length(&w2, n+m, _state);
|
|
ae_vector_set_length(&tmp, m, _state);
|
|
ae_vector_set_length(&tmpdiff, m, _state);
|
|
ae_matrix_set_length(&fmatrix, n+m, m, _state);
|
|
if( k>0 )
|
|
{
|
|
ae_matrix_set_length(&cmatrix, k, m+1, _state);
|
|
}
|
|
|
|
/*
|
|
* Fill design matrix, Y2, W2:
|
|
* * first N rows with basis functions for original points
|
|
* * next M rows with decay terms
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* prepare Ith row
|
|
* use Tmp for calculations to avoid multidimensional arrays overhead
|
|
*/
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
if( j==0 )
|
|
{
|
|
tmp.ptr.p_double[j] = 1;
|
|
}
|
|
else
|
|
{
|
|
if( j==1 )
|
|
{
|
|
tmp.ptr.p_double[j] = x->ptr.p_double[i];
|
|
}
|
|
else
|
|
{
|
|
tmp.ptr.p_double[j] = 2*x->ptr.p_double[i]*tmp.ptr.p_double[j-1]-tmp.ptr.p_double[j-2];
|
|
}
|
|
}
|
|
}
|
|
ae_v_move(&fmatrix.ptr.pp_double[i][0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
if( i==j )
|
|
{
|
|
fmatrix.ptr.pp_double[n+i][j] = decay;
|
|
}
|
|
else
|
|
{
|
|
fmatrix.ptr.pp_double[n+i][j] = 0;
|
|
}
|
|
}
|
|
}
|
|
ae_v_move(&y2.ptr.p_double[0], 1, &y->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
ae_v_move(&w2.ptr.p_double[0], 1, &w->ptr.p_double[0], 1, ae_v_len(0,n-1));
|
|
mx = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
mx = mx+ae_fabs(w->ptr.p_double[i], _state);
|
|
}
|
|
mx = mx/n;
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
y2.ptr.p_double[n+i] = 0;
|
|
w2.ptr.p_double[n+i] = mx;
|
|
}
|
|
|
|
/*
|
|
* fill constraints matrix
|
|
*/
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
|
|
/*
|
|
* prepare Ith row
|
|
* use Tmp for basis function values,
|
|
* TmpDiff for basos function derivatives
|
|
*/
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
if( j==0 )
|
|
{
|
|
tmp.ptr.p_double[j] = 1;
|
|
tmpdiff.ptr.p_double[j] = 0;
|
|
}
|
|
else
|
|
{
|
|
if( j==1 )
|
|
{
|
|
tmp.ptr.p_double[j] = xc->ptr.p_double[i];
|
|
tmpdiff.ptr.p_double[j] = 1;
|
|
}
|
|
else
|
|
{
|
|
tmp.ptr.p_double[j] = 2*xc->ptr.p_double[i]*tmp.ptr.p_double[j-1]-tmp.ptr.p_double[j-2];
|
|
tmpdiff.ptr.p_double[j] = 2*(tmp.ptr.p_double[j-1]+xc->ptr.p_double[i]*tmpdiff.ptr.p_double[j-1])-tmpdiff.ptr.p_double[j-2];
|
|
}
|
|
}
|
|
}
|
|
if( dc->ptr.p_int[i]==0 )
|
|
{
|
|
ae_v_move(&cmatrix.ptr.pp_double[i][0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
}
|
|
if( dc->ptr.p_int[i]==1 )
|
|
{
|
|
ae_v_move(&cmatrix.ptr.pp_double[i][0], 1, &tmpdiff.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
}
|
|
cmatrix.ptr.pp_double[i][m] = yc->ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Solve constrained task
|
|
*/
|
|
if( k>0 )
|
|
{
|
|
|
|
/*
|
|
* solve using regularization
|
|
*/
|
|
lsfitlinearwc(&y2, &w2, &fmatrix, &cmatrix, n+m, m, k, info, c, rep, _state);
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* no constraints, no regularization needed
|
|
*/
|
|
lsfitlinearwc(y, w, &fmatrix, &cmatrix, n, m, 0, info, c, rep, _state);
|
|
}
|
|
if( *info<0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal Floater-Hormann fitting subroutine for fixed D
|
|
*************************************************************************/
|
|
static void lsfit_barycentricfitwcfixedd(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* xc,
|
|
/* Real */ ae_vector* yc,
|
|
/* Integer */ ae_vector* dc,
|
|
ae_int_t k,
|
|
ae_int_t m,
|
|
ae_int_t d,
|
|
ae_int_t* info,
|
|
barycentricinterpolant* b,
|
|
barycentricfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_vector _w;
|
|
ae_vector _xc;
|
|
ae_vector _yc;
|
|
ae_matrix fmatrix;
|
|
ae_matrix cmatrix;
|
|
ae_vector y2;
|
|
ae_vector w2;
|
|
ae_vector sx;
|
|
ae_vector sy;
|
|
ae_vector sbf;
|
|
ae_vector xoriginal;
|
|
ae_vector yoriginal;
|
|
ae_vector tmp;
|
|
lsfitreport lrep;
|
|
double v0;
|
|
double v1;
|
|
double mx;
|
|
barycentricinterpolant b2;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t relcnt;
|
|
double xa;
|
|
double xb;
|
|
double sa;
|
|
double sb;
|
|
double decay;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
ae_vector_init_copy(&_w, w, _state, ae_true);
|
|
w = &_w;
|
|
ae_vector_init_copy(&_xc, xc, _state, ae_true);
|
|
xc = &_xc;
|
|
ae_vector_init_copy(&_yc, yc, _state, ae_true);
|
|
yc = &_yc;
|
|
*info = 0;
|
|
_barycentricinterpolant_clear(b);
|
|
_barycentricfitreport_clear(rep);
|
|
ae_matrix_init(&fmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&cmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&w2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sy, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&sbf, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xoriginal, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&yoriginal, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
_lsfitreport_init(&lrep, _state, ae_true);
|
|
_barycentricinterpolant_init(&b2, _state, ae_true);
|
|
|
|
if( ((n<1||m<2)||k<0)||k>=m )
|
|
{
|
|
*info = -1;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
*info = 0;
|
|
if( dc->ptr.p_int[i]<0 )
|
|
{
|
|
*info = -1;
|
|
}
|
|
if( dc->ptr.p_int[i]>1 )
|
|
{
|
|
*info = -1;
|
|
}
|
|
if( *info<0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* weight decay for correct handling of task which becomes
|
|
* degenerate after constraints are applied
|
|
*/
|
|
decay = 10000*ae_machineepsilon;
|
|
|
|
/*
|
|
* Scale X, Y, XC, YC
|
|
*/
|
|
lsfitscalexy(x, y, w, n, xc, yc, dc, k, &xa, &xb, &sa, &sb, &xoriginal, &yoriginal, _state);
|
|
|
|
/*
|
|
* allocate space, initialize:
|
|
* * FMatrix- values of basis functions at X[]
|
|
* * CMatrix- values (derivatives) of basis functions at XC[]
|
|
*/
|
|
ae_vector_set_length(&y2, n+m, _state);
|
|
ae_vector_set_length(&w2, n+m, _state);
|
|
ae_matrix_set_length(&fmatrix, n+m, m, _state);
|
|
if( k>0 )
|
|
{
|
|
ae_matrix_set_length(&cmatrix, k, m+1, _state);
|
|
}
|
|
ae_vector_set_length(&y2, n+m, _state);
|
|
ae_vector_set_length(&w2, n+m, _state);
|
|
|
|
/*
|
|
* Prepare design and constraints matrices:
|
|
* * fill constraints matrix
|
|
* * fill first N rows of design matrix with values
|
|
* * fill next M rows of design matrix with regularizing term
|
|
* * append M zeros to Y
|
|
* * append M elements, mean(abs(W)) each, to W
|
|
*/
|
|
ae_vector_set_length(&sx, m, _state);
|
|
ae_vector_set_length(&sy, m, _state);
|
|
ae_vector_set_length(&sbf, m, _state);
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
sx.ptr.p_double[j] = (double)(2*j)/(double)(m-1)-1;
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sy.ptr.p_double[i] = 1;
|
|
}
|
|
barycentricbuildfloaterhormann(&sx, &sy, m, d, &b2, _state);
|
|
mx = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
lsfit_barycentriccalcbasis(&b2, x->ptr.p_double[i], &sbf, _state);
|
|
ae_v_move(&fmatrix.ptr.pp_double[i][0], 1, &sbf.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
y2.ptr.p_double[i] = y->ptr.p_double[i];
|
|
w2.ptr.p_double[i] = w->ptr.p_double[i];
|
|
mx = mx+ae_fabs(w->ptr.p_double[i], _state)/n;
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
if( i==j )
|
|
{
|
|
fmatrix.ptr.pp_double[n+i][j] = decay;
|
|
}
|
|
else
|
|
{
|
|
fmatrix.ptr.pp_double[n+i][j] = 0;
|
|
}
|
|
}
|
|
y2.ptr.p_double[n+i] = 0;
|
|
w2.ptr.p_double[n+i] = mx;
|
|
}
|
|
if( k>0 )
|
|
{
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sy.ptr.p_double[i] = 0;
|
|
}
|
|
sy.ptr.p_double[j] = 1;
|
|
barycentricbuildfloaterhormann(&sx, &sy, m, d, &b2, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
ae_assert(dc->ptr.p_int[i]>=0&&dc->ptr.p_int[i]<=1, "BarycentricFit: internal error!", _state);
|
|
barycentricdiff1(&b2, xc->ptr.p_double[i], &v0, &v1, _state);
|
|
if( dc->ptr.p_int[i]==0 )
|
|
{
|
|
cmatrix.ptr.pp_double[i][j] = v0;
|
|
}
|
|
if( dc->ptr.p_int[i]==1 )
|
|
{
|
|
cmatrix.ptr.pp_double[i][j] = v1;
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
cmatrix.ptr.pp_double[i][m] = yc->ptr.p_double[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Solve constrained task
|
|
*/
|
|
if( k>0 )
|
|
{
|
|
|
|
/*
|
|
* solve using regularization
|
|
*/
|
|
lsfitlinearwc(&y2, &w2, &fmatrix, &cmatrix, n+m, m, k, info, &tmp, &lrep, _state);
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* no constraints, no regularization needed
|
|
*/
|
|
lsfitlinearwc(y, w, &fmatrix, &cmatrix, n, m, k, info, &tmp, &lrep, _state);
|
|
}
|
|
if( *info<0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Generate interpolant and scale it
|
|
*/
|
|
ae_v_move(&sy.ptr.p_double[0], 1, &tmp.ptr.p_double[0], 1, ae_v_len(0,m-1));
|
|
barycentricbuildfloaterhormann(&sx, &sy, m, d, b, _state);
|
|
barycentriclintransx(b, 2/(xb-xa), -(xa+xb)/(xb-xa), _state);
|
|
barycentriclintransy(b, sb-sa, sa, _state);
|
|
|
|
/*
|
|
* Scale absolute errors obtained from LSFitLinearW.
|
|
* Relative error should be calculated separately
|
|
* (because of shifting/scaling of the task)
|
|
*/
|
|
rep->taskrcond = lrep.taskrcond;
|
|
rep->rmserror = lrep.rmserror*(sb-sa);
|
|
rep->avgerror = lrep.avgerror*(sb-sa);
|
|
rep->maxerror = lrep.maxerror*(sb-sa);
|
|
rep->avgrelerror = 0;
|
|
relcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(yoriginal.ptr.p_double[i],0) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror+ae_fabs(barycentriccalc(b, xoriginal.ptr.p_double[i], _state)-yoriginal.ptr.p_double[i], _state)/ae_fabs(yoriginal.ptr.p_double[i], _state);
|
|
relcnt = relcnt+1;
|
|
}
|
|
}
|
|
if( relcnt!=0 )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror/relcnt;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
static void lsfit_clearreport(lsfitreport* rep, ae_state *_state)
|
|
{
|
|
|
|
|
|
rep->taskrcond = 0;
|
|
rep->iterationscount = 0;
|
|
rep->varidx = -1;
|
|
rep->rmserror = 0;
|
|
rep->avgerror = 0;
|
|
rep->avgrelerror = 0;
|
|
rep->maxerror = 0;
|
|
rep->wrmserror = 0;
|
|
rep->r2 = 0;
|
|
ae_matrix_set_length(&rep->covpar, 0, 0, _state);
|
|
ae_vector_set_length(&rep->errpar, 0, _state);
|
|
ae_vector_set_length(&rep->errcurve, 0, _state);
|
|
ae_vector_set_length(&rep->noise, 0, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This internal function estimates covariance matrix and other error-related
|
|
information for linear/nonlinear least squares model.
|
|
|
|
It has a bit awkward interface, but it can be used for both linear and
|
|
nonlinear problems.
|
|
|
|
INPUT PARAMETERS:
|
|
F1 - array[0..N-1,0..K-1]:
|
|
* for linear problems - matrix of function values
|
|
* for nonlinear problems - Jacobian matrix
|
|
F0 - array[0..N-1]:
|
|
* for linear problems - must be filled with zeros
|
|
* for nonlinear problems - must store values of function being
|
|
fitted
|
|
Y - array[0..N-1]:
|
|
* for linear and nonlinear problems - must store target values
|
|
W - weights, array[0..N-1]:
|
|
* for linear and nonlinear problems - weights
|
|
X - array[0..K-1]:
|
|
* for linear and nonlinear problems - current solution
|
|
S - array[0..K-1]:
|
|
* its components should be strictly positive
|
|
* squared inverse of this diagonal matrix is used as damping
|
|
factor for covariance matrix (linear and nonlinear problems)
|
|
* for nonlinear problems, when scale of the variables is usually
|
|
explicitly given by user, you may use scale vector for this
|
|
parameter
|
|
* for linear problems you may set this parameter to
|
|
S=sqrt(1/diag(F'*F))
|
|
* this parameter is automatically rescaled by this function,
|
|
only relative magnitudes of its components (with respect to
|
|
each other) matter.
|
|
N - number of points, N>0.
|
|
K - number of dimensions
|
|
Rep - structure which is used to store results
|
|
Z - additional matrix which, depending on ZKind, may contain some
|
|
information used to accelerate calculations - or just can be
|
|
temporary buffer:
|
|
* for ZKind=0 Z contains no information, just temporary
|
|
buffer which can be resized and used as needed
|
|
* for ZKind=1 Z contains triangular matrix from QR
|
|
decomposition of W*F1. This matrix can be used
|
|
to speedup calculation of covariance matrix.
|
|
It should not be changed by algorithm.
|
|
ZKind- contents of Z
|
|
|
|
OUTPUT PARAMETERS:
|
|
|
|
* Rep.CovPar covariance matrix for parameters, array[K,K].
|
|
* Rep.ErrPar errors in parameters, array[K],
|
|
errpar = sqrt(diag(CovPar))
|
|
* Rep.ErrCurve vector of fit errors - standard deviations of empirical
|
|
best-fit curve from "ideal" best-fit curve built with
|
|
infinite number of samples, array[N].
|
|
errcurve = sqrt(diag(J*CovPar*J')),
|
|
where J is Jacobian matrix.
|
|
* Rep.Noise vector of per-point estimates of noise, array[N]
|
|
* Rep.R2 coefficient of determination (non-weighted)
|
|
|
|
Other fields of Rep are not changed.
|
|
|
|
IMPORTANT: errors in parameters are calculated without taking into
|
|
account boundary/linear constraints! Presence of constraints
|
|
changes distribution of errors, but there is no easy way to
|
|
account for constraints when you calculate covariance matrix.
|
|
|
|
NOTE: noise in the data is estimated as follows:
|
|
* for fitting without user-supplied weights all points are
|
|
assumed to have same level of noise, which is estimated from
|
|
the data
|
|
* for fitting with user-supplied weights we assume that noise
|
|
level in I-th point is inversely proportional to Ith weight.
|
|
Coefficient of proportionality is estimated from the data.
|
|
|
|
NOTE: we apply small amount of regularization when we invert squared
|
|
Jacobian and calculate covariance matrix. It guarantees that
|
|
algorithm won't divide by zero during inversion, but skews
|
|
error estimates a bit (fractional error is about 10^-9).
|
|
|
|
However, we believe that this difference is insignificant for
|
|
all practical purposes except for the situation when you want
|
|
to compare ALGLIB results with "reference" implementation up
|
|
to the last significant digit.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void lsfit_estimateerrors(/* Real */ ae_matrix* f1,
|
|
/* Real */ ae_vector* f0,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_vector* w,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* s,
|
|
ae_int_t n,
|
|
ae_int_t k,
|
|
lsfitreport* rep,
|
|
/* Real */ ae_matrix* z,
|
|
ae_int_t zkind,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _s;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t j1;
|
|
double v;
|
|
double noisec;
|
|
ae_int_t info;
|
|
matinvreport invrep;
|
|
ae_int_t nzcnt;
|
|
double avg;
|
|
double rss;
|
|
double tss;
|
|
double sz;
|
|
double ss;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_s, s, _state, ae_true);
|
|
s = &_s;
|
|
_matinvreport_init(&invrep, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Compute NZCnt - count of non-zero weights
|
|
*/
|
|
nzcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(w->ptr.p_double[i],0) )
|
|
{
|
|
nzcnt = nzcnt+1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Compute R2
|
|
*/
|
|
if( nzcnt>0 )
|
|
{
|
|
avg = 0.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(w->ptr.p_double[i],0) )
|
|
{
|
|
avg = avg+y->ptr.p_double[i];
|
|
}
|
|
}
|
|
avg = avg/nzcnt;
|
|
rss = 0.0;
|
|
tss = 0.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(w->ptr.p_double[i],0) )
|
|
{
|
|
v = ae_v_dotproduct(&f1->ptr.pp_double[i][0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
v = v+f0->ptr.p_double[i];
|
|
rss = rss+ae_sqr(v-y->ptr.p_double[i], _state);
|
|
tss = tss+ae_sqr(y->ptr.p_double[i]-avg, _state);
|
|
}
|
|
}
|
|
if( ae_fp_neq(tss,0) )
|
|
{
|
|
rep->r2 = ae_maxreal(1.0-rss/tss, 0.0, _state);
|
|
}
|
|
else
|
|
{
|
|
rep->r2 = 1.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rep->r2 = 0;
|
|
}
|
|
|
|
/*
|
|
* Compute estimate of proportionality between noise in the data and weights:
|
|
* NoiseC = mean(per-point-noise*per-point-weight)
|
|
* Noise level (standard deviation) at each point is equal to NoiseC/W[I].
|
|
*/
|
|
if( nzcnt>k )
|
|
{
|
|
noisec = 0.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(w->ptr.p_double[i],0) )
|
|
{
|
|
v = ae_v_dotproduct(&f1->ptr.pp_double[i][0], 1, &x->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
v = v+f0->ptr.p_double[i];
|
|
noisec = noisec+ae_sqr((v-y->ptr.p_double[i])*w->ptr.p_double[i], _state);
|
|
}
|
|
}
|
|
noisec = ae_sqrt(noisec/(nzcnt-k), _state);
|
|
}
|
|
else
|
|
{
|
|
noisec = 0.0;
|
|
}
|
|
|
|
/*
|
|
* Two branches on noise level:
|
|
* * NoiseC>0 normal situation
|
|
* * NoiseC=0 degenerate case CovPar is filled by zeros
|
|
*/
|
|
rmatrixsetlengthatleast(&rep->covpar, k, k, _state);
|
|
if( ae_fp_greater(noisec,0) )
|
|
{
|
|
|
|
/*
|
|
* Normal situation: non-zero noise level
|
|
*/
|
|
ae_assert(zkind==0||zkind==1, "LSFit: internal error in EstimateErrors() function", _state);
|
|
if( zkind==0 )
|
|
{
|
|
|
|
/*
|
|
* Z contains no additional information which can be used to speed up
|
|
* calculations. We have to calculate covariance matrix on our own:
|
|
* * Compute scaled Jacobian N*J, where N[i,i]=WCur[I]/NoiseC, store in Z
|
|
* * Compute Z'*Z, store in CovPar
|
|
* * Apply moderate regularization to CovPar and compute matrix inverse.
|
|
* In case inverse failed, increase regularization parameter and try
|
|
* again.
|
|
*/
|
|
rmatrixsetlengthatleast(z, n, k, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = w->ptr.p_double[i]/noisec;
|
|
ae_v_moved(&z->ptr.pp_double[i][0], 1, &f1->ptr.pp_double[i][0], 1, ae_v_len(0,k-1), v);
|
|
}
|
|
|
|
/*
|
|
* Convert S to automatically scaled damped matrix:
|
|
* * calculate SZ - sum of diagonal elements of Z'*Z
|
|
* * calculate SS - sum of diagonal elements of S^(-2)
|
|
* * overwrite S by (SZ/SS)*S^(-2)
|
|
* * now S has approximately same magnitude as giagonal of Z'*Z
|
|
*/
|
|
sz = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
sz = sz+z->ptr.pp_double[i][j]*z->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
if( ae_fp_eq(sz,0) )
|
|
{
|
|
sz = 1;
|
|
}
|
|
ss = 0;
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
ss = ss+1/ae_sqr(s->ptr.p_double[j], _state);
|
|
}
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
s->ptr.p_double[j] = sz/ss/ae_sqr(s->ptr.p_double[j], _state);
|
|
}
|
|
|
|
/*
|
|
* Calculate damped inverse inv(Z'*Z+S).
|
|
* We increase damping factor V until Z'*Z become well-conditioned.
|
|
*/
|
|
v = 1.0E3*ae_machineepsilon;
|
|
do
|
|
{
|
|
rmatrixsyrk(k, n, 1.0, z, 0, 0, 2, 0.0, &rep->covpar, 0, 0, ae_true, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
rep->covpar.ptr.pp_double[i][i] = rep->covpar.ptr.pp_double[i][i]+v*s->ptr.p_double[i];
|
|
}
|
|
spdmatrixinverse(&rep->covpar, k, ae_true, &info, &invrep, _state);
|
|
v = 10*v;
|
|
}
|
|
while(info<=0);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=i+1; j<=k-1; j++)
|
|
{
|
|
rep->covpar.ptr.pp_double[j][i] = rep->covpar.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
if( zkind==1 )
|
|
{
|
|
|
|
/*
|
|
* We can reuse additional information:
|
|
* * Z contains R matrix from QR decomposition of W*F1
|
|
* * After multiplication by 1/NoiseC we get Z_mod = N*F1, where diag(N)=w[i]/NoiseC
|
|
* * Such triangular Z_mod is a Cholesky factor from decomposition of J'*N'*N*J.
|
|
* Thus, we can calculate covariance matrix as inverse of the matrix given by
|
|
* its Cholesky decomposition. It allow us to avoid time-consuming calculation
|
|
* of J'*N'*N*J in CovPar - complexity is reduced from O(N*K^2) to O(K^3), which
|
|
* is quite good because K is usually orders of magnitude smaller than N.
|
|
*
|
|
* First, convert S to automatically scaled damped matrix:
|
|
* * calculate SZ - sum of magnitudes of diagonal elements of Z/NoiseC
|
|
* * calculate SS - sum of diagonal elements of S^(-1)
|
|
* * overwrite S by (SZ/SS)*S^(-1)
|
|
* * now S has approximately same magnitude as giagonal of Z'*Z
|
|
*/
|
|
sz = 0;
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
sz = sz+ae_fabs(z->ptr.pp_double[j][j]/noisec, _state);
|
|
}
|
|
if( ae_fp_eq(sz,0) )
|
|
{
|
|
sz = 1;
|
|
}
|
|
ss = 0;
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
ss = ss+1/s->ptr.p_double[j];
|
|
}
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
s->ptr.p_double[j] = sz/ss/s->ptr.p_double[j];
|
|
}
|
|
|
|
/*
|
|
* Calculate damped inverse of inv((Z+v*S)'*(Z+v*S))
|
|
* We increase damping factor V until matrix become well-conditioned.
|
|
*/
|
|
v = 1.0E3*ae_machineepsilon;
|
|
do
|
|
{
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=i; j<=k-1; j++)
|
|
{
|
|
rep->covpar.ptr.pp_double[i][j] = z->ptr.pp_double[i][j]/noisec;
|
|
}
|
|
rep->covpar.ptr.pp_double[i][i] = rep->covpar.ptr.pp_double[i][i]+v*s->ptr.p_double[i];
|
|
}
|
|
spdmatrixcholeskyinverse(&rep->covpar, k, ae_true, &info, &invrep, _state);
|
|
v = 10*v;
|
|
}
|
|
while(info<=0);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=i+1; j<=k-1; j++)
|
|
{
|
|
rep->covpar.ptr.pp_double[j][i] = rep->covpar.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Degenerate situation: zero noise level, covariance matrix is zero.
|
|
*/
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
rep->covpar.ptr.pp_double[j][i] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Estimate erorrs in parameters, curve and per-point noise
|
|
*/
|
|
rvectorsetlengthatleast(&rep->errpar, k, _state);
|
|
rvectorsetlengthatleast(&rep->errcurve, n, _state);
|
|
rvectorsetlengthatleast(&rep->noise, n, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
rep->errpar.ptr.p_double[i] = ae_sqrt(rep->covpar.ptr.pp_double[i][i], _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* ErrCurve[I] is sqrt(P[i,i]) where P=J*CovPar*J'
|
|
*/
|
|
v = 0.0;
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
for(j1=0; j1<=k-1; j1++)
|
|
{
|
|
v = v+f1->ptr.pp_double[i][j]*rep->covpar.ptr.pp_double[j][j1]*f1->ptr.pp_double[i][j1];
|
|
}
|
|
}
|
|
rep->errcurve.ptr.p_double[i] = ae_sqrt(v, _state);
|
|
|
|
/*
|
|
* Noise[i] is filled using weights and current estimate of noise level
|
|
*/
|
|
if( ae_fp_neq(w->ptr.p_double[i],0) )
|
|
{
|
|
rep->noise.ptr.p_double[i] = noisec/w->ptr.p_double[i];
|
|
}
|
|
else
|
|
{
|
|
rep->noise.ptr.p_double[i] = 0;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
ae_bool _polynomialfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
polynomialfitreport *p = (polynomialfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _polynomialfitreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
polynomialfitreport *dst = (polynomialfitreport*)_dst;
|
|
polynomialfitreport *src = (polynomialfitreport*)_src;
|
|
dst->taskrcond = src->taskrcond;
|
|
dst->rmserror = src->rmserror;
|
|
dst->avgerror = src->avgerror;
|
|
dst->avgrelerror = src->avgrelerror;
|
|
dst->maxerror = src->maxerror;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _polynomialfitreport_clear(void* _p)
|
|
{
|
|
polynomialfitreport *p = (polynomialfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _polynomialfitreport_destroy(void* _p)
|
|
{
|
|
polynomialfitreport *p = (polynomialfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
ae_bool _barycentricfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
barycentricfitreport *p = (barycentricfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _barycentricfitreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
barycentricfitreport *dst = (barycentricfitreport*)_dst;
|
|
barycentricfitreport *src = (barycentricfitreport*)_src;
|
|
dst->taskrcond = src->taskrcond;
|
|
dst->dbest = src->dbest;
|
|
dst->rmserror = src->rmserror;
|
|
dst->avgerror = src->avgerror;
|
|
dst->avgrelerror = src->avgrelerror;
|
|
dst->maxerror = src->maxerror;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _barycentricfitreport_clear(void* _p)
|
|
{
|
|
barycentricfitreport *p = (barycentricfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _barycentricfitreport_destroy(void* _p)
|
|
{
|
|
barycentricfitreport *p = (barycentricfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
ae_bool _spline1dfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline1dfitreport *p = (spline1dfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _spline1dfitreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline1dfitreport *dst = (spline1dfitreport*)_dst;
|
|
spline1dfitreport *src = (spline1dfitreport*)_src;
|
|
dst->taskrcond = src->taskrcond;
|
|
dst->rmserror = src->rmserror;
|
|
dst->avgerror = src->avgerror;
|
|
dst->avgrelerror = src->avgrelerror;
|
|
dst->maxerror = src->maxerror;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _spline1dfitreport_clear(void* _p)
|
|
{
|
|
spline1dfitreport *p = (spline1dfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _spline1dfitreport_destroy(void* _p)
|
|
{
|
|
spline1dfitreport *p = (spline1dfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
ae_bool _lsfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
lsfitreport *p = (lsfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_matrix_init(&p->covpar, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->errpar, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->errcurve, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->noise, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _lsfitreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
lsfitreport *dst = (lsfitreport*)_dst;
|
|
lsfitreport *src = (lsfitreport*)_src;
|
|
dst->taskrcond = src->taskrcond;
|
|
dst->iterationscount = src->iterationscount;
|
|
dst->varidx = src->varidx;
|
|
dst->rmserror = src->rmserror;
|
|
dst->avgerror = src->avgerror;
|
|
dst->avgrelerror = src->avgrelerror;
|
|
dst->maxerror = src->maxerror;
|
|
dst->wrmserror = src->wrmserror;
|
|
if( !ae_matrix_init_copy(&dst->covpar, &src->covpar, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->errpar, &src->errpar, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->errcurve, &src->errcurve, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->noise, &src->noise, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->r2 = src->r2;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _lsfitreport_clear(void* _p)
|
|
{
|
|
lsfitreport *p = (lsfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_matrix_clear(&p->covpar);
|
|
ae_vector_clear(&p->errpar);
|
|
ae_vector_clear(&p->errcurve);
|
|
ae_vector_clear(&p->noise);
|
|
}
|
|
|
|
|
|
void _lsfitreport_destroy(void* _p)
|
|
{
|
|
lsfitreport *p = (lsfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_matrix_destroy(&p->covpar);
|
|
ae_vector_destroy(&p->errpar);
|
|
ae_vector_destroy(&p->errcurve);
|
|
ae_vector_destroy(&p->noise);
|
|
}
|
|
|
|
|
|
ae_bool _lsfitstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
lsfitstate *p = (lsfitstate*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->taskx, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->tasky, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->taskw, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->h, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->wcur, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->tmp, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->tmpf, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->tmpjac, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->tmpjacw, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_matinvreport_init(&p->invrep, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_lsfitreport_init(&p->rep, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_minlmstate_init(&p->optstate, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_minlmreport_init(&p->optrep, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_rcommstate_init(&p->rstate, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _lsfitstate_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
lsfitstate *dst = (lsfitstate*)_dst;
|
|
lsfitstate *src = (lsfitstate*)_src;
|
|
dst->optalgo = src->optalgo;
|
|
dst->m = src->m;
|
|
dst->k = src->k;
|
|
dst->epsf = src->epsf;
|
|
dst->epsx = src->epsx;
|
|
dst->maxits = src->maxits;
|
|
dst->stpmax = src->stpmax;
|
|
dst->xrep = src->xrep;
|
|
if( !ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->taskx, &src->taskx, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->tasky, &src->tasky, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->npoints = src->npoints;
|
|
if( !ae_vector_init_copy(&dst->taskw, &src->taskw, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->nweights = src->nweights;
|
|
dst->wkind = src->wkind;
|
|
dst->wits = src->wits;
|
|
dst->diffstep = src->diffstep;
|
|
dst->teststep = src->teststep;
|
|
dst->xupdated = src->xupdated;
|
|
dst->needf = src->needf;
|
|
dst->needfg = src->needfg;
|
|
dst->needfgh = src->needfgh;
|
|
dst->pointindex = src->pointindex;
|
|
if( !ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->f = src->f;
|
|
if( !ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->h, &src->h, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->wcur, &src->wcur, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->tmp, &src->tmp, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->tmpf, &src->tmpf, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->tmpjac, &src->tmpjac, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->tmpjacw, &src->tmpjacw, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->tmpnoise = src->tmpnoise;
|
|
if( !_matinvreport_init_copy(&dst->invrep, &src->invrep, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->repiterationscount = src->repiterationscount;
|
|
dst->repterminationtype = src->repterminationtype;
|
|
dst->repvaridx = src->repvaridx;
|
|
dst->reprmserror = src->reprmserror;
|
|
dst->repavgerror = src->repavgerror;
|
|
dst->repavgrelerror = src->repavgrelerror;
|
|
dst->repmaxerror = src->repmaxerror;
|
|
dst->repwrmserror = src->repwrmserror;
|
|
if( !_lsfitreport_init_copy(&dst->rep, &src->rep, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_minlmstate_init_copy(&dst->optstate, &src->optstate, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_minlmreport_init_copy(&dst->optrep, &src->optrep, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->prevnpt = src->prevnpt;
|
|
dst->prevalgo = src->prevalgo;
|
|
if( !_rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _lsfitstate_clear(void* _p)
|
|
{
|
|
lsfitstate *p = (lsfitstate*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->s);
|
|
ae_vector_clear(&p->bndl);
|
|
ae_vector_clear(&p->bndu);
|
|
ae_matrix_clear(&p->taskx);
|
|
ae_vector_clear(&p->tasky);
|
|
ae_vector_clear(&p->taskw);
|
|
ae_vector_clear(&p->x);
|
|
ae_vector_clear(&p->c);
|
|
ae_vector_clear(&p->g);
|
|
ae_matrix_clear(&p->h);
|
|
ae_vector_clear(&p->wcur);
|
|
ae_vector_clear(&p->tmp);
|
|
ae_vector_clear(&p->tmpf);
|
|
ae_matrix_clear(&p->tmpjac);
|
|
ae_matrix_clear(&p->tmpjacw);
|
|
_matinvreport_clear(&p->invrep);
|
|
_lsfitreport_clear(&p->rep);
|
|
_minlmstate_clear(&p->optstate);
|
|
_minlmreport_clear(&p->optrep);
|
|
_rcommstate_clear(&p->rstate);
|
|
}
|
|
|
|
|
|
void _lsfitstate_destroy(void* _p)
|
|
{
|
|
lsfitstate *p = (lsfitstate*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->s);
|
|
ae_vector_destroy(&p->bndl);
|
|
ae_vector_destroy(&p->bndu);
|
|
ae_matrix_destroy(&p->taskx);
|
|
ae_vector_destroy(&p->tasky);
|
|
ae_vector_destroy(&p->taskw);
|
|
ae_vector_destroy(&p->x);
|
|
ae_vector_destroy(&p->c);
|
|
ae_vector_destroy(&p->g);
|
|
ae_matrix_destroy(&p->h);
|
|
ae_vector_destroy(&p->wcur);
|
|
ae_vector_destroy(&p->tmp);
|
|
ae_vector_destroy(&p->tmpf);
|
|
ae_matrix_destroy(&p->tmpjac);
|
|
ae_matrix_destroy(&p->tmpjacw);
|
|
_matinvreport_destroy(&p->invrep);
|
|
_lsfitreport_destroy(&p->rep);
|
|
_minlmstate_destroy(&p->optstate);
|
|
_minlmreport_destroy(&p->optrep);
|
|
_rcommstate_destroy(&p->rstate);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
This function builds non-periodic 2-dimensional parametric spline which
|
|
starts at (X[0],Y[0]) and ends at (X[N-1],Y[N-1]).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - points, array[0..N-1,0..1].
|
|
XY[I,0:1] corresponds to the Ith point.
|
|
Order of points is important!
|
|
N - points count, N>=5 for Akima splines, N>=2 for other types of
|
|
splines.
|
|
ST - spline type:
|
|
* 0 Akima spline
|
|
* 1 parabolically terminated Catmull-Rom spline (Tension=0)
|
|
* 2 parabolically terminated cubic spline
|
|
PT - parameterization type:
|
|
* 0 uniform
|
|
* 1 chord length
|
|
* 2 centripetal
|
|
|
|
OUTPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
|
|
|
|
NOTES:
|
|
* this function assumes that there all consequent points are distinct.
|
|
I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2), (x2,y2)<>(x3,y3) and so on.
|
|
However, non-consequent points may coincide, i.e. we can have (x0,y0)=
|
|
=(x2,y2).
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2build(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t st,
|
|
ae_int_t pt,
|
|
pspline2interpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_matrix _xy;
|
|
ae_vector tmp;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_init_copy(&_xy, xy, _state, ae_true);
|
|
xy = &_xy;
|
|
_pspline2interpolant_clear(p);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(st>=0&&st<=2, "PSpline2Build: incorrect spline type!", _state);
|
|
ae_assert(pt>=0&&pt<=2, "PSpline2Build: incorrect parameterization type!", _state);
|
|
if( st==0 )
|
|
{
|
|
ae_assert(n>=5, "PSpline2Build: N<5 (minimum value for Akima splines)!", _state);
|
|
}
|
|
else
|
|
{
|
|
ae_assert(n>=2, "PSpline2Build: N<2!", _state);
|
|
}
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
p->n = n;
|
|
p->periodic = ae_false;
|
|
ae_vector_set_length(&tmp, n, _state);
|
|
|
|
/*
|
|
* Build parameterization, check that all parameters are distinct
|
|
*/
|
|
pspline_pspline2par(xy, n, pt, &p->p, _state);
|
|
ae_assert(aredistinct(&p->p, n, _state), "PSpline2Build: consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Build splines
|
|
*/
|
|
if( st==0 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildakima(&p->p, &tmp, n, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildakima(&p->p, &tmp, n, &p->y, _state);
|
|
}
|
|
if( st==1 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n, 0, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n, 0, 0.0, &p->y, _state);
|
|
}
|
|
if( st==2 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcubic(&p->p, &tmp, n, 0, 0.0, 0, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcubic(&p->p, &tmp, n, 0, 0.0, 0, 0.0, &p->y, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function builds non-periodic 3-dimensional parametric spline which
|
|
starts at (X[0],Y[0],Z[0]) and ends at (X[N-1],Y[N-1],Z[N-1]).
|
|
|
|
Same as PSpline2Build() function, but for 3D, so we won't duplicate its
|
|
description here.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3build(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t st,
|
|
ae_int_t pt,
|
|
pspline3interpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_matrix _xy;
|
|
ae_vector tmp;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_init_copy(&_xy, xy, _state, ae_true);
|
|
xy = &_xy;
|
|
_pspline3interpolant_clear(p);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(st>=0&&st<=2, "PSpline3Build: incorrect spline type!", _state);
|
|
ae_assert(pt>=0&&pt<=2, "PSpline3Build: incorrect parameterization type!", _state);
|
|
if( st==0 )
|
|
{
|
|
ae_assert(n>=5, "PSpline3Build: N<5 (minimum value for Akima splines)!", _state);
|
|
}
|
|
else
|
|
{
|
|
ae_assert(n>=2, "PSpline3Build: N<2!", _state);
|
|
}
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
p->n = n;
|
|
p->periodic = ae_false;
|
|
ae_vector_set_length(&tmp, n, _state);
|
|
|
|
/*
|
|
* Build parameterization, check that all parameters are distinct
|
|
*/
|
|
pspline_pspline3par(xy, n, pt, &p->p, _state);
|
|
ae_assert(aredistinct(&p->p, n, _state), "PSpline3Build: consequent points are too close!", _state);
|
|
|
|
/*
|
|
* Build splines
|
|
*/
|
|
if( st==0 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildakima(&p->p, &tmp, n, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildakima(&p->p, &tmp, n, &p->y, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][2], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildakima(&p->p, &tmp, n, &p->z, _state);
|
|
}
|
|
if( st==1 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n, 0, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n, 0, 0.0, &p->y, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][2], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n, 0, 0.0, &p->z, _state);
|
|
}
|
|
if( st==2 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcubic(&p->p, &tmp, n, 0, 0.0, 0, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcubic(&p->p, &tmp, n, 0, 0.0, 0, 0.0, &p->y, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xy->ptr.pp_double[0][2], xy->stride, ae_v_len(0,n-1));
|
|
spline1dbuildcubic(&p->p, &tmp, n, 0, 0.0, 0, 0.0, &p->z, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function builds periodic 2-dimensional parametric spline which
|
|
starts at (X[0],Y[0]), goes through all points to (X[N-1],Y[N-1]) and then
|
|
back to (X[0],Y[0]).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - points, array[0..N-1,0..1].
|
|
XY[I,0:1] corresponds to the Ith point.
|
|
XY[N-1,0:1] must be different from XY[0,0:1].
|
|
Order of points is important!
|
|
N - points count, N>=3 for other types of splines.
|
|
ST - spline type:
|
|
* 1 Catmull-Rom spline (Tension=0) with cyclic boundary conditions
|
|
* 2 cubic spline with cyclic boundary conditions
|
|
PT - parameterization type:
|
|
* 0 uniform
|
|
* 1 chord length
|
|
* 2 centripetal
|
|
|
|
OUTPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
|
|
|
|
NOTES:
|
|
* this function assumes that there all consequent points are distinct.
|
|
I.e. (x0,y0)<>(x1,y1), (x1,y1)<>(x2,y2), (x2,y2)<>(x3,y3) and so on.
|
|
However, non-consequent points may coincide, i.e. we can have (x0,y0)=
|
|
=(x2,y2).
|
|
* last point of sequence is NOT equal to the first point. You shouldn't
|
|
make curve "explicitly periodic" by making them equal.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2buildperiodic(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t st,
|
|
ae_int_t pt,
|
|
pspline2interpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_matrix _xy;
|
|
ae_matrix xyp;
|
|
ae_vector tmp;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_init_copy(&_xy, xy, _state, ae_true);
|
|
xy = &_xy;
|
|
_pspline2interpolant_clear(p);
|
|
ae_matrix_init(&xyp, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(st>=1&&st<=2, "PSpline2BuildPeriodic: incorrect spline type!", _state);
|
|
ae_assert(pt>=0&&pt<=2, "PSpline2BuildPeriodic: incorrect parameterization type!", _state);
|
|
ae_assert(n>=3, "PSpline2BuildPeriodic: N<3!", _state);
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
p->n = n;
|
|
p->periodic = ae_true;
|
|
ae_vector_set_length(&tmp, n+1, _state);
|
|
ae_matrix_set_length(&xyp, n+1, 2, _state);
|
|
ae_v_move(&xyp.ptr.pp_double[0][0], xyp.stride, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
ae_v_move(&xyp.ptr.pp_double[0][1], xyp.stride, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
ae_v_move(&xyp.ptr.pp_double[n][0], 1, &xy->ptr.pp_double[0][0], 1, ae_v_len(0,1));
|
|
|
|
/*
|
|
* Build parameterization, check that all parameters are distinct
|
|
*/
|
|
pspline_pspline2par(&xyp, n+1, pt, &p->p, _state);
|
|
ae_assert(aredistinct(&p->p, n+1, _state), "PSpline2BuildPeriodic: consequent (or first and last) points are too close!", _state);
|
|
|
|
/*
|
|
* Build splines
|
|
*/
|
|
if( st==1 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][0], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n+1, -1, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][1], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n+1, -1, 0.0, &p->y, _state);
|
|
}
|
|
if( st==2 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][0], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcubic(&p->p, &tmp, n+1, -1, 0.0, -1, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][1], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcubic(&p->p, &tmp, n+1, -1, 0.0, -1, 0.0, &p->y, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function builds periodic 3-dimensional parametric spline which
|
|
starts at (X[0],Y[0],Z[0]), goes through all points to (X[N-1],Y[N-1],Z[N-1])
|
|
and then back to (X[0],Y[0],Z[0]).
|
|
|
|
Same as PSpline2Build() function, but for 3D, so we won't duplicate its
|
|
description here.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3buildperiodic(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t st,
|
|
ae_int_t pt,
|
|
pspline3interpolant* p,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_matrix _xy;
|
|
ae_matrix xyp;
|
|
ae_vector tmp;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_init_copy(&_xy, xy, _state, ae_true);
|
|
xy = &_xy;
|
|
_pspline3interpolant_clear(p);
|
|
ae_matrix_init(&xyp, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(st>=1&&st<=2, "PSpline3BuildPeriodic: incorrect spline type!", _state);
|
|
ae_assert(pt>=0&&pt<=2, "PSpline3BuildPeriodic: incorrect parameterization type!", _state);
|
|
ae_assert(n>=3, "PSpline3BuildPeriodic: N<3!", _state);
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
p->n = n;
|
|
p->periodic = ae_true;
|
|
ae_vector_set_length(&tmp, n+1, _state);
|
|
ae_matrix_set_length(&xyp, n+1, 3, _state);
|
|
ae_v_move(&xyp.ptr.pp_double[0][0], xyp.stride, &xy->ptr.pp_double[0][0], xy->stride, ae_v_len(0,n-1));
|
|
ae_v_move(&xyp.ptr.pp_double[0][1], xyp.stride, &xy->ptr.pp_double[0][1], xy->stride, ae_v_len(0,n-1));
|
|
ae_v_move(&xyp.ptr.pp_double[0][2], xyp.stride, &xy->ptr.pp_double[0][2], xy->stride, ae_v_len(0,n-1));
|
|
ae_v_move(&xyp.ptr.pp_double[n][0], 1, &xy->ptr.pp_double[0][0], 1, ae_v_len(0,2));
|
|
|
|
/*
|
|
* Build parameterization, check that all parameters are distinct
|
|
*/
|
|
pspline_pspline3par(&xyp, n+1, pt, &p->p, _state);
|
|
ae_assert(aredistinct(&p->p, n+1, _state), "PSplineBuild2Periodic: consequent (or first and last) points are too close!", _state);
|
|
|
|
/*
|
|
* Build splines
|
|
*/
|
|
if( st==1 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][0], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n+1, -1, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][1], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n+1, -1, 0.0, &p->y, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][2], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcatmullrom(&p->p, &tmp, n+1, -1, 0.0, &p->z, _state);
|
|
}
|
|
if( st==2 )
|
|
{
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][0], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcubic(&p->p, &tmp, n+1, -1, 0.0, -1, 0.0, &p->x, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][1], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcubic(&p->p, &tmp, n+1, -1, 0.0, -1, 0.0, &p->y, _state);
|
|
ae_v_move(&tmp.ptr.p_double[0], 1, &xyp.ptr.pp_double[0][2], xyp.stride, ae_v_len(0,n));
|
|
spline1dbuildcubic(&p->p, &tmp, n+1, -1, 0.0, -1, 0.0, &p->z, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function returns vector of parameter values correspoding to points.
|
|
|
|
I.e. for P created from (X[0],Y[0])...(X[N-1],Y[N-1]) and U=TValues(P) we
|
|
have
|
|
(X[0],Y[0]) = PSpline2Calc(P,U[0]),
|
|
(X[1],Y[1]) = PSpline2Calc(P,U[1]),
|
|
(X[2],Y[2]) = PSpline2Calc(P,U[2]),
|
|
...
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
|
|
OUTPUT PARAMETERS:
|
|
N - array size
|
|
T - array[0..N-1]
|
|
|
|
|
|
NOTES:
|
|
* for non-periodic splines U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]=1
|
|
* for periodic splines U[0]=0, U[0]<U[1]<...<U[N-1], U[N-1]<1
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2parametervalues(pspline2interpolant* p,
|
|
ae_int_t* n,
|
|
/* Real */ ae_vector* t,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*n = 0;
|
|
ae_vector_clear(t);
|
|
|
|
ae_assert(p->n>=2, "PSpline2ParameterValues: internal error!", _state);
|
|
*n = p->n;
|
|
ae_vector_set_length(t, *n, _state);
|
|
ae_v_move(&t->ptr.p_double[0], 1, &p->p.ptr.p_double[0], 1, ae_v_len(0,*n-1));
|
|
t->ptr.p_double[0] = 0;
|
|
if( !p->periodic )
|
|
{
|
|
t->ptr.p_double[*n-1] = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function returns vector of parameter values correspoding to points.
|
|
|
|
Same as PSpline2ParameterValues(), but for 3D.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3parametervalues(pspline3interpolant* p,
|
|
ae_int_t* n,
|
|
/* Real */ ae_vector* t,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*n = 0;
|
|
ae_vector_clear(t);
|
|
|
|
ae_assert(p->n>=2, "PSpline3ParameterValues: internal error!", _state);
|
|
*n = p->n;
|
|
ae_vector_set_length(t, *n, _state);
|
|
ae_v_move(&t->ptr.p_double[0], 1, &p->p.ptr.p_double[0], 1, ae_v_len(0,*n-1));
|
|
t->ptr.p_double[0] = 0;
|
|
if( !p->periodic )
|
|
{
|
|
t->ptr.p_double[*n-1] = 1;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates the value of the parametric spline for a given
|
|
value of parameter T
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-position
|
|
Y - Y-position
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2calc(pspline2interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* y,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*x = 0;
|
|
*y = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
*x = spline1dcalc(&p->x, t, _state);
|
|
*y = spline1dcalc(&p->y, t, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates the value of the parametric spline for a given
|
|
value of parameter T.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-position
|
|
Y - Y-position
|
|
Z - Z-position
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3calc(pspline3interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* y,
|
|
double* z,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*x = 0;
|
|
*y = 0;
|
|
*z = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
*x = spline1dcalc(&p->x, t, _state);
|
|
*y = spline1dcalc(&p->y, t, _state);
|
|
*z = spline1dcalc(&p->z, t, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates tangent vector for a given value of parameter T
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-component of tangent vector (normalized)
|
|
Y - Y-component of tangent vector (normalized)
|
|
|
|
NOTE:
|
|
X^2+Y^2 is either 1 (for non-zero tangent vector) or 0.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2tangent(pspline2interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* y,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
double v0;
|
|
double v1;
|
|
|
|
*x = 0;
|
|
*y = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
pspline2diff(p, t, &v0, x, &v1, y, _state);
|
|
if( ae_fp_neq(*x,0)||ae_fp_neq(*y,0) )
|
|
{
|
|
|
|
/*
|
|
* this code is a bit more complex than X^2+Y^2 to avoid
|
|
* overflow for large values of X and Y.
|
|
*/
|
|
v = safepythag2(*x, *y, _state);
|
|
*x = *x/v;
|
|
*y = *y/v;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates tangent vector for a given value of parameter T
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-component of tangent vector (normalized)
|
|
Y - Y-component of tangent vector (normalized)
|
|
Z - Z-component of tangent vector (normalized)
|
|
|
|
NOTE:
|
|
X^2+Y^2+Z^2 is either 1 (for non-zero tangent vector) or 0.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3tangent(pspline3interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* y,
|
|
double* z,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
double v0;
|
|
double v1;
|
|
double v2;
|
|
|
|
*x = 0;
|
|
*y = 0;
|
|
*z = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
pspline3diff(p, t, &v0, x, &v1, y, &v2, z, _state);
|
|
if( (ae_fp_neq(*x,0)||ae_fp_neq(*y,0))||ae_fp_neq(*z,0) )
|
|
{
|
|
v = safepythag3(*x, *y, *z, _state);
|
|
*x = *x/v;
|
|
*y = *y/v;
|
|
*z = *z/v;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates derivative, i.e. it returns (dX/dT,dY/dT).
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - X-derivative
|
|
Y - Y-value
|
|
DY - Y-derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2diff(pspline2interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* dx,
|
|
double* y,
|
|
double* dy,
|
|
ae_state *_state)
|
|
{
|
|
double d2s;
|
|
|
|
*x = 0;
|
|
*dx = 0;
|
|
*y = 0;
|
|
*dy = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
spline1ddiff(&p->x, t, x, dx, &d2s, _state);
|
|
spline1ddiff(&p->y, t, y, dy, &d2s, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates derivative, i.e. it returns (dX/dT,dY/dT,dZ/dT).
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - X-derivative
|
|
Y - Y-value
|
|
DY - Y-derivative
|
|
Z - Z-value
|
|
DZ - Z-derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3diff(pspline3interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* dx,
|
|
double* y,
|
|
double* dy,
|
|
double* z,
|
|
double* dz,
|
|
ae_state *_state)
|
|
{
|
|
double d2s;
|
|
|
|
*x = 0;
|
|
*dx = 0;
|
|
*y = 0;
|
|
*dy = 0;
|
|
*z = 0;
|
|
*dz = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
spline1ddiff(&p->x, t, x, dx, &d2s, _state);
|
|
spline1ddiff(&p->y, t, y, dy, &d2s, _state);
|
|
spline1ddiff(&p->z, t, z, dz, &d2s, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates first and second derivative with respect to T.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - derivative
|
|
D2X - second derivative
|
|
Y - Y-value
|
|
DY - derivative
|
|
D2Y - second derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline2diff2(pspline2interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* dx,
|
|
double* d2x,
|
|
double* y,
|
|
double* dy,
|
|
double* d2y,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*x = 0;
|
|
*dx = 0;
|
|
*d2x = 0;
|
|
*y = 0;
|
|
*dy = 0;
|
|
*d2y = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
spline1ddiff(&p->x, t, x, dx, d2x, _state);
|
|
spline1ddiff(&p->y, t, y, dy, d2y, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates first and second derivative with respect to T.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
T - point:
|
|
* T in [0,1] corresponds to interval spanned by points
|
|
* for non-periodic splines T<0 (or T>1) correspond to parts of
|
|
the curve before the first (after the last) point
|
|
* for periodic splines T<0 (or T>1) are projected into [0,1]
|
|
by making T=T-floor(T).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X - X-value
|
|
DX - derivative
|
|
D2X - second derivative
|
|
Y - Y-value
|
|
DY - derivative
|
|
D2Y - second derivative
|
|
Z - Z-value
|
|
DZ - derivative
|
|
D2Z - second derivative
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void pspline3diff2(pspline3interpolant* p,
|
|
double t,
|
|
double* x,
|
|
double* dx,
|
|
double* d2x,
|
|
double* y,
|
|
double* dy,
|
|
double* d2y,
|
|
double* z,
|
|
double* dz,
|
|
double* d2z,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*x = 0;
|
|
*dx = 0;
|
|
*d2x = 0;
|
|
*y = 0;
|
|
*dy = 0;
|
|
*d2y = 0;
|
|
*z = 0;
|
|
*dz = 0;
|
|
*d2z = 0;
|
|
|
|
if( p->periodic )
|
|
{
|
|
t = t-ae_ifloor(t, _state);
|
|
}
|
|
spline1ddiff(&p->x, t, x, dx, d2x, _state);
|
|
spline1ddiff(&p->y, t, y, dy, d2y, _state);
|
|
spline1ddiff(&p->z, t, z, dz, d2z, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates arc length, i.e. length of curve between t=a
|
|
and t=b.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
A,B - parameter values corresponding to arc ends:
|
|
* B>A will result in positive length returned
|
|
* B<A will result in negative length returned
|
|
|
|
RESULT:
|
|
length of arc starting at T=A and ending at T=B.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double pspline2arclength(pspline2interpolant* p,
|
|
double a,
|
|
double b,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
autogkstate state;
|
|
autogkreport rep;
|
|
double sx;
|
|
double dsx;
|
|
double d2sx;
|
|
double sy;
|
|
double dsy;
|
|
double d2sy;
|
|
double result;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_autogkstate_init(&state, _state, ae_true);
|
|
_autogkreport_init(&rep, _state, ae_true);
|
|
|
|
autogksmooth(a, b, &state, _state);
|
|
while(autogkiteration(&state, _state))
|
|
{
|
|
spline1ddiff(&p->x, state.x, &sx, &dsx, &d2sx, _state);
|
|
spline1ddiff(&p->y, state.x, &sy, &dsy, &d2sy, _state);
|
|
state.f = safepythag2(dsx, dsy, _state);
|
|
}
|
|
autogkresults(&state, &result, &rep, _state);
|
|
ae_assert(rep.terminationtype>0, "PSpline2ArcLength: internal error!", _state);
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates arc length, i.e. length of curve between t=a
|
|
and t=b.
|
|
|
|
INPUT PARAMETERS:
|
|
P - parametric spline interpolant
|
|
A,B - parameter values corresponding to arc ends:
|
|
* B>A will result in positive length returned
|
|
* B<A will result in negative length returned
|
|
|
|
RESULT:
|
|
length of arc starting at T=A and ending at T=B.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.05.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double pspline3arclength(pspline3interpolant* p,
|
|
double a,
|
|
double b,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
autogkstate state;
|
|
autogkreport rep;
|
|
double sx;
|
|
double dsx;
|
|
double d2sx;
|
|
double sy;
|
|
double dsy;
|
|
double d2sy;
|
|
double sz;
|
|
double dsz;
|
|
double d2sz;
|
|
double result;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_autogkstate_init(&state, _state, ae_true);
|
|
_autogkreport_init(&rep, _state, ae_true);
|
|
|
|
autogksmooth(a, b, &state, _state);
|
|
while(autogkiteration(&state, _state))
|
|
{
|
|
spline1ddiff(&p->x, state.x, &sx, &dsx, &d2sx, _state);
|
|
spline1ddiff(&p->y, state.x, &sy, &dsy, &d2sy, _state);
|
|
spline1ddiff(&p->z, state.x, &sz, &dsz, &d2sz, _state);
|
|
state.f = safepythag3(dsx, dsy, dsz, _state);
|
|
}
|
|
autogkresults(&state, &result, &rep, _state);
|
|
ae_assert(rep.terminationtype>0, "PSpline3ArcLength: internal error!", _state);
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Builds non-periodic parameterization for 2-dimensional spline
|
|
*************************************************************************/
|
|
static void pspline_pspline2par(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t pt,
|
|
/* Real */ ae_vector* p,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
ae_int_t i;
|
|
|
|
ae_vector_clear(p);
|
|
|
|
ae_assert(pt>=0&&pt<=2, "PSpline2Par: internal error!", _state);
|
|
|
|
/*
|
|
* Build parameterization:
|
|
* * fill by non-normalized values
|
|
* * normalize them so we have P[0]=0, P[N-1]=1.
|
|
*/
|
|
ae_vector_set_length(p, n, _state);
|
|
if( pt==0 )
|
|
{
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
p->ptr.p_double[i] = i;
|
|
}
|
|
}
|
|
if( pt==1 )
|
|
{
|
|
p->ptr.p_double[0] = 0;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
p->ptr.p_double[i] = p->ptr.p_double[i-1]+safepythag2(xy->ptr.pp_double[i][0]-xy->ptr.pp_double[i-1][0], xy->ptr.pp_double[i][1]-xy->ptr.pp_double[i-1][1], _state);
|
|
}
|
|
}
|
|
if( pt==2 )
|
|
{
|
|
p->ptr.p_double[0] = 0;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
p->ptr.p_double[i] = p->ptr.p_double[i-1]+ae_sqrt(safepythag2(xy->ptr.pp_double[i][0]-xy->ptr.pp_double[i-1][0], xy->ptr.pp_double[i][1]-xy->ptr.pp_double[i-1][1], _state), _state);
|
|
}
|
|
}
|
|
v = 1/p->ptr.p_double[n-1];
|
|
ae_v_muld(&p->ptr.p_double[0], 1, ae_v_len(0,n-1), v);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Builds non-periodic parameterization for 3-dimensional spline
|
|
*************************************************************************/
|
|
static void pspline_pspline3par(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t pt,
|
|
/* Real */ ae_vector* p,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
ae_int_t i;
|
|
|
|
ae_vector_clear(p);
|
|
|
|
ae_assert(pt>=0&&pt<=2, "PSpline3Par: internal error!", _state);
|
|
|
|
/*
|
|
* Build parameterization:
|
|
* * fill by non-normalized values
|
|
* * normalize them so we have P[0]=0, P[N-1]=1.
|
|
*/
|
|
ae_vector_set_length(p, n, _state);
|
|
if( pt==0 )
|
|
{
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
p->ptr.p_double[i] = i;
|
|
}
|
|
}
|
|
if( pt==1 )
|
|
{
|
|
p->ptr.p_double[0] = 0;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
p->ptr.p_double[i] = p->ptr.p_double[i-1]+safepythag3(xy->ptr.pp_double[i][0]-xy->ptr.pp_double[i-1][0], xy->ptr.pp_double[i][1]-xy->ptr.pp_double[i-1][1], xy->ptr.pp_double[i][2]-xy->ptr.pp_double[i-1][2], _state);
|
|
}
|
|
}
|
|
if( pt==2 )
|
|
{
|
|
p->ptr.p_double[0] = 0;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
p->ptr.p_double[i] = p->ptr.p_double[i-1]+ae_sqrt(safepythag3(xy->ptr.pp_double[i][0]-xy->ptr.pp_double[i-1][0], xy->ptr.pp_double[i][1]-xy->ptr.pp_double[i-1][1], xy->ptr.pp_double[i][2]-xy->ptr.pp_double[i-1][2], _state), _state);
|
|
}
|
|
}
|
|
v = 1/p->ptr.p_double[n-1];
|
|
ae_v_muld(&p->ptr.p_double[0], 1, ae_v_len(0,n-1), v);
|
|
}
|
|
|
|
|
|
ae_bool _pspline2interpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
pspline2interpolant *p = (pspline2interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_vector_init(&p->p, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init(&p->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init(&p->y, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _pspline2interpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
pspline2interpolant *dst = (pspline2interpolant*)_dst;
|
|
pspline2interpolant *src = (pspline2interpolant*)_src;
|
|
dst->n = src->n;
|
|
dst->periodic = src->periodic;
|
|
if( !ae_vector_init_copy(&dst->p, &src->p, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init_copy(&dst->y, &src->y, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _pspline2interpolant_clear(void* _p)
|
|
{
|
|
pspline2interpolant *p = (pspline2interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->p);
|
|
_spline1dinterpolant_clear(&p->x);
|
|
_spline1dinterpolant_clear(&p->y);
|
|
}
|
|
|
|
|
|
void _pspline2interpolant_destroy(void* _p)
|
|
{
|
|
pspline2interpolant *p = (pspline2interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->p);
|
|
_spline1dinterpolant_destroy(&p->x);
|
|
_spline1dinterpolant_destroy(&p->y);
|
|
}
|
|
|
|
|
|
ae_bool _pspline3interpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
pspline3interpolant *p = (pspline3interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_vector_init(&p->p, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init(&p->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init(&p->y, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init(&p->z, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _pspline3interpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
pspline3interpolant *dst = (pspline3interpolant*)_dst;
|
|
pspline3interpolant *src = (pspline3interpolant*)_src;
|
|
dst->n = src->n;
|
|
dst->periodic = src->periodic;
|
|
if( !ae_vector_init_copy(&dst->p, &src->p, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init_copy(&dst->y, &src->y, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !_spline1dinterpolant_init_copy(&dst->z, &src->z, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _pspline3interpolant_clear(void* _p)
|
|
{
|
|
pspline3interpolant *p = (pspline3interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->p);
|
|
_spline1dinterpolant_clear(&p->x);
|
|
_spline1dinterpolant_clear(&p->y);
|
|
_spline1dinterpolant_clear(&p->z);
|
|
}
|
|
|
|
|
|
void _pspline3interpolant_destroy(void* _p)
|
|
{
|
|
pspline3interpolant *p = (pspline3interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->p);
|
|
_spline1dinterpolant_destroy(&p->x);
|
|
_spline1dinterpolant_destroy(&p->y);
|
|
_spline1dinterpolant_destroy(&p->z);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
This function creates RBF model for a scalar (NY=1) or vector (NY>1)
|
|
function in a NX-dimensional space (NX=2 or NX=3).
|
|
|
|
Newly created model is empty. It can be used for interpolation right after
|
|
creation, but it just returns zeros. You have to add points to the model,
|
|
tune interpolation settings, and then call model construction function
|
|
RBFBuildModel() which will update model according to your specification.
|
|
|
|
USAGE:
|
|
1. User creates model with RBFCreate()
|
|
2. User adds dataset with RBFSetPoints() (points do NOT have to be on a
|
|
regular grid)
|
|
3. (OPTIONAL) User chooses polynomial term by calling:
|
|
* RBFLinTerm() to set linear term
|
|
* RBFConstTerm() to set constant term
|
|
* RBFZeroTerm() to set zero term
|
|
By default, linear term is used.
|
|
4. User chooses specific RBF algorithm to use: either QNN (RBFSetAlgoQNN)
|
|
or ML (RBFSetAlgoMultiLayer).
|
|
5. User calls RBFBuildModel() function which rebuilds model according to
|
|
the specification
|
|
6. User may call RBFCalc() to calculate model value at the specified point,
|
|
RBFGridCalc() to calculate model values at the points of the regular
|
|
grid. User may extract model coefficients with RBFUnpack() call.
|
|
|
|
INPUT PARAMETERS:
|
|
NX - dimension of the space, NX=2 or NX=3
|
|
NY - function dimension, NY>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - RBF model (initially equals to zero)
|
|
|
|
NOTE 1: memory requirements. RBF models require amount of memory which is
|
|
proportional to the number of data points. Memory is allocated
|
|
during model construction, but most of this memory is freed after
|
|
model coefficients are calculated.
|
|
|
|
Some approximate estimates for N centers with default settings are
|
|
given below:
|
|
* about 250*N*(sizeof(double)+2*sizeof(int)) bytes of memory is
|
|
needed during model construction stage.
|
|
* about 15*N*sizeof(double) bytes is needed after model is built.
|
|
For example, for N=100000 we may need 0.6 GB of memory to build
|
|
model, but just about 0.012 GB to store it.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcreate(ae_int_t nx, ae_int_t ny, rbfmodel* s, ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
_rbfmodel_clear(s);
|
|
|
|
ae_assert(nx==2||nx==3, "RBFCreate: NX<>2 and NX<>3", _state);
|
|
ae_assert(ny>=1, "RBFCreate: NY<1", _state);
|
|
s->nx = nx;
|
|
s->ny = ny;
|
|
s->nl = 0;
|
|
s->nc = 0;
|
|
ae_matrix_set_length(&s->v, ny, rbf_mxnx+1, _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = 0;
|
|
}
|
|
}
|
|
s->n = 0;
|
|
s->rmax = 0;
|
|
s->gridtype = 2;
|
|
s->fixrad = ae_false;
|
|
s->radvalue = 1;
|
|
s->radzvalue = 5;
|
|
s->aterm = 1;
|
|
s->algorithmtype = 1;
|
|
|
|
/*
|
|
* stopping criteria
|
|
*/
|
|
s->epsort = rbf_eps;
|
|
s->epserr = rbf_eps;
|
|
s->maxits = 0;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function adds dataset.
|
|
|
|
This function overrides results of the previous calls, i.e. multiple calls
|
|
of this function will result in only the last set being added.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call.
|
|
XY - points, array[N,NX+NY]. One row corresponds to one point
|
|
in the dataset. First NX elements are coordinates, next
|
|
NY elements are function values. Array may be larger than
|
|
specific, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
|
|
After you've added dataset and (optionally) tuned algorithm settings you
|
|
should call RBFBuildModel() in order to build a model for you.
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetpoints(rbfmodel* s,
|
|
/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
|
|
ae_assert(n>0, "RBFSetPoints: N<0", _state);
|
|
ae_assert(xy->rows>=n, "RBFSetPoints: Rows(XY)<N", _state);
|
|
ae_assert(xy->cols>=s->nx+s->ny, "RBFSetPoints: Cols(XY)<NX+NY", _state);
|
|
s->n = n;
|
|
ae_matrix_set_length(&s->x, s->n, rbf_mxnx, _state);
|
|
ae_matrix_set_length(&s->y, s->n, s->ny, _state);
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
s->x.ptr.pp_double[i][j] = 0;
|
|
}
|
|
for(j=0; j<=s->nx-1; j++)
|
|
{
|
|
s->x.ptr.pp_double[i][j] = xy->ptr.pp_double[i][j];
|
|
}
|
|
for(j=0; j<=s->ny-1; j++)
|
|
{
|
|
s->y.ptr.pp_double[i][j] = xy->ptr.pp_double[i][j+s->nx];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called RBF-QNN and it is good for point sets with
|
|
following properties:
|
|
a) all points are distinct
|
|
b) all points are well separated.
|
|
c) points distribution is approximately uniform. There is no "contour
|
|
lines", clusters of points, or other small-scale structures.
|
|
|
|
Algorithm description:
|
|
1) interpolation centers are allocated to data points
|
|
2) interpolation radii are calculated as distances to the nearest centers
|
|
times Q coefficient (where Q is a value from [0.75,1.50]).
|
|
3) after performing (2) radii are transformed in order to avoid situation
|
|
when single outlier has very large radius and influences many points
|
|
across all dataset. Transformation has following form:
|
|
new_r[i] = min(r[i],Z*median(r[]))
|
|
where r[i] is I-th radius, median() is a median radius across entire
|
|
dataset, Z is user-specified value which controls amount of deviation
|
|
from median radius.
|
|
|
|
When (a) is violated, we will be unable to build RBF model. When (b) or
|
|
(c) are violated, model will be built, but interpolation quality will be
|
|
low. See http://www.alglib.net/interpolation/ for more information on this
|
|
subject.
|
|
|
|
This algorithm is used by default.
|
|
|
|
Additional Q parameter controls smoothness properties of the RBF basis:
|
|
* Q<0.75 will give perfectly conditioned basis, but terrible smoothness
|
|
properties (RBF interpolant will have sharp peaks around function values)
|
|
* Q around 1.0 gives good balance between smoothness and condition number
|
|
* Q>1.5 will lead to badly conditioned systems and slow convergence of the
|
|
underlying linear solver (although smoothness will be very good)
|
|
* Q>2.0 will effectively make optimizer useless because it won't converge
|
|
within reasonable amount of iterations. It is possible to set such large
|
|
Q, but it is advised not to do so.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
Q - Q parameter, Q>0, recommended value - 1.0
|
|
Z - Z parameter, Z>0, recommended value - 5.0
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgoqnn(rbfmodel* s, double q, double z, ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(q, _state), "RBFSetAlgoQNN: Q is infinite or NAN", _state);
|
|
ae_assert(ae_fp_greater(q,0), "RBFSetAlgoQNN: Q<=0", _state);
|
|
rbf_rbfgridpoints(s, _state);
|
|
rbf_rbfradnn(s, q, z, _state);
|
|
s->algorithmtype = 1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called RBF-ML. It builds multilayer RBF model, i.e.
|
|
model with subsequently decreasing radii, which allows us to combine
|
|
smoothness (due to large radii of the first layers) with exactness (due
|
|
to small radii of the last layers) and fast convergence.
|
|
|
|
Internally RBF-ML uses many different means of acceleration, from sparse
|
|
matrices to KD-trees, which results in algorithm whose working time is
|
|
roughly proportional to N*log(N)*Density*RBase^2*NLayers, where N is a
|
|
number of points, Density is an average density if points per unit of the
|
|
interpolation space, RBase is an initial radius, NLayers is a number of
|
|
layers.
|
|
|
|
RBF-ML is good for following kinds of interpolation problems:
|
|
1. "exact" problems (perfect fit) with well separated points
|
|
2. least squares problems with arbitrary distribution of points (algorithm
|
|
gives perfect fit where it is possible, and resorts to least squares
|
|
fit in the hard areas).
|
|
3. noisy problems where we want to apply some controlled amount of
|
|
smoothing.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
RBase - RBase parameter, RBase>0
|
|
NLayers - NLayers parameter, NLayers>0, recommended value to start
|
|
with - about 5.
|
|
LambdaV - regularization value, can be useful when solving problem
|
|
in the least squares sense. Optimal lambda is problem-
|
|
dependent and require trial and error. In our experience,
|
|
good lambda can be as large as 0.1, and you can use 0.001
|
|
as initial guess.
|
|
Default value - 0.01, which is used when LambdaV is not
|
|
given. You can specify zero value, but it is not
|
|
recommended to do so.
|
|
|
|
TUNING ALGORITHM
|
|
|
|
In order to use this algorithm you have to choose three parameters:
|
|
* initial radius RBase
|
|
* number of layers in the model NLayers
|
|
* regularization coefficient LambdaV
|
|
|
|
Initial radius is easy to choose - you can pick any number several times
|
|
larger than the average distance between points. Algorithm won't break
|
|
down if you choose radius which is too large (model construction time will
|
|
increase, but model will be built correctly).
|
|
|
|
Choose such number of layers that RLast=RBase/2^(NLayers-1) (radius used
|
|
by the last layer) will be smaller than the typical distance between
|
|
points. In case model error is too large, you can increase number of
|
|
layers. Having more layers will make model construction and evaluation
|
|
proportionally slower, but it will allow you to have model which precisely
|
|
fits your data. From the other side, if you want to suppress noise, you
|
|
can DECREASE number of layers to make your model less flexible.
|
|
|
|
Regularization coefficient LambdaV controls smoothness of the individual
|
|
models built for each layer. We recommend you to use default value in case
|
|
you don't want to tune this parameter, because having non-zero LambdaV
|
|
accelerates and stabilizes internal iterative algorithm. In case you want
|
|
to suppress noise you can use LambdaV as additional parameter (larger
|
|
value = more smoothness) to tune.
|
|
|
|
TYPICAL ERRORS
|
|
|
|
1. Using initial radius which is too large. Memory requirements of the
|
|
RBF-ML are roughly proportional to N*Density*RBase^2 (where Density is
|
|
an average density of points per unit of the interpolation space). In
|
|
the extreme case of the very large RBase we will need O(N^2) units of
|
|
memory - and many layers in order to decrease radius to some reasonably
|
|
small value.
|
|
|
|
2. Using too small number of layers - RBF models with large radius are not
|
|
flexible enough to reproduce small variations in the target function.
|
|
You need many layers with different radii, from large to small, in
|
|
order to have good model.
|
|
|
|
3. Using initial radius which is too small. You will get model with
|
|
"holes" in the areas which are too far away from interpolation centers.
|
|
However, algorithm will work correctly (and quickly) in this case.
|
|
|
|
4. Using too many layers - you will get too large and too slow model. This
|
|
model will perfectly reproduce your function, but maybe you will be
|
|
able to achieve similar results with less layers (and less memory).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.03.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgomultilayer(rbfmodel* s,
|
|
double rbase,
|
|
ae_int_t nlayers,
|
|
double lambdav,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(rbase, _state), "RBFSetAlgoMultiLayer: RBase is infinite or NaN", _state);
|
|
ae_assert(ae_fp_greater(rbase,0), "RBFSetAlgoMultiLayer: RBase<=0", _state);
|
|
ae_assert(nlayers>=0, "RBFSetAlgoMultiLayer: NLayers<0", _state);
|
|
ae_assert(ae_isfinite(lambdav, _state), "RBFSetAlgoMultiLayer: LambdaV is infinite or NAN", _state);
|
|
ae_assert(ae_fp_greater_eq(lambdav,0), "RBFSetAlgoMultiLayer: LambdaV<0", _state);
|
|
s->radvalue = rbase;
|
|
s->nlayers = nlayers;
|
|
s->algorithmtype = 2;
|
|
s->lambdav = lambdav;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets linear term (model is a sum of radial basis functions
|
|
plus linear polynomial). This function won't have effect until next call
|
|
to RBFBuildModel().
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetlinterm(rbfmodel* s, ae_state *_state)
|
|
{
|
|
|
|
|
|
s->aterm = 1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets constant term (model is a sum of radial basis functions
|
|
plus constant). This function won't have effect until next call to
|
|
RBFBuildModel().
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetconstterm(rbfmodel* s, ae_state *_state)
|
|
{
|
|
|
|
|
|
s->aterm = 2;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets zero term (model is a sum of radial basis functions
|
|
without polynomial term). This function won't have effect until next call
|
|
to RBFBuildModel().
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetzeroterm(rbfmodel* s, ae_state *_state)
|
|
{
|
|
|
|
|
|
s->aterm = 3;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets stopping criteria of the underlying linear solver.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
EpsOrt - orthogonality stopping criterion, EpsOrt>=0. Algorithm will
|
|
stop when ||A'*r||<=EpsOrt where A' is a transpose of the
|
|
system matrix, r is a residual vector.
|
|
Recommended value of EpsOrt is equal to 1E-6.
|
|
This criterion will stop algorithm when we have "bad fit"
|
|
situation, i.e. when we should stop in a point with large,
|
|
nonzero residual.
|
|
EpsErr - residual stopping criterion. Algorithm will stop when
|
|
||r||<=EpsErr*||b||, where r is a residual vector, b is a
|
|
right part of the system (function values).
|
|
Recommended value of EpsErr is equal to 1E-3 or 1E-6.
|
|
This criterion will stop algorithm in a "good fit"
|
|
situation when we have near-zero residual near the desired
|
|
solution.
|
|
MaxIts - this criterion will stop algorithm after MaxIts iterations.
|
|
It should be used for debugging purposes only!
|
|
Zero MaxIts means that no limit is placed on the number of
|
|
iterations.
|
|
|
|
We recommend to set moderate non-zero values EpsOrt and EpsErr
|
|
simultaneously. Values equal to 10E-6 are good to start with. In case you
|
|
need high performance and do not need high precision , you may decrease
|
|
EpsErr down to 0.001. However, we do not recommend decreasing EpsOrt.
|
|
|
|
As for MaxIts, we recommend to leave it zero unless you know what you do.
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetcond(rbfmodel* s,
|
|
double epsort,
|
|
double epserr,
|
|
ae_int_t maxits,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(epsort, _state)&&ae_fp_greater_eq(epsort,0), "RBFSetCond: EpsOrt is negative, INF or NAN", _state);
|
|
ae_assert(ae_isfinite(epserr, _state)&&ae_fp_greater_eq(epserr,0), "RBFSetCond: EpsB is negative, INF or NAN", _state);
|
|
ae_assert(maxits>=0, "RBFSetCond: MaxIts is negative", _state);
|
|
if( (ae_fp_eq(epsort,0)&&ae_fp_eq(epserr,0))&&maxits==0 )
|
|
{
|
|
s->epsort = rbf_eps;
|
|
s->epserr = rbf_eps;
|
|
s->maxits = 0;
|
|
}
|
|
else
|
|
{
|
|
s->epsort = epsort;
|
|
s->epserr = epserr;
|
|
s->maxits = maxits;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function builds RBF model and returns report (contains some
|
|
information which can be used for evaluation of the algorithm properties).
|
|
|
|
Call to this function modifies RBF model by calculating its centers/radii/
|
|
weights and saving them into RBFModel structure. Initially RBFModel
|
|
contain zero coefficients, but after call to this function we will have
|
|
coefficients which were calculated in order to fit our dataset.
|
|
|
|
After you called this function you can call RBFCalc(), RBFGridCalc() and
|
|
other model calculation functions.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
Rep - report:
|
|
* Rep.TerminationType:
|
|
* -5 - non-distinct basis function centers were detected,
|
|
interpolation aborted
|
|
* -4 - nonconvergence of the internal SVD solver
|
|
* 1 - successful termination
|
|
Fields are used for debugging purposes:
|
|
* Rep.IterationsCount - iterations count of the LSQR solver
|
|
* Rep.NMV - number of matrix-vector products
|
|
* Rep.ARows - rows count for the system matrix
|
|
* Rep.ACols - columns count for the system matrix
|
|
* Rep.ANNZ - number of significantly non-zero elements
|
|
(elements above some algorithm-determined threshold)
|
|
|
|
NOTE: failure to build model will leave current state of the structure
|
|
unchanged.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfbuildmodel(rbfmodel* s, rbfreport* rep, ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
kdtree tree;
|
|
kdtree ctree;
|
|
ae_vector dist;
|
|
ae_vector xcx;
|
|
ae_matrix a;
|
|
ae_matrix v;
|
|
ae_matrix omega;
|
|
ae_vector y;
|
|
ae_matrix residualy;
|
|
ae_vector radius;
|
|
ae_matrix xc;
|
|
ae_vector mnx;
|
|
ae_vector mxx;
|
|
ae_vector edge;
|
|
ae_vector mxsteps;
|
|
ae_int_t nc;
|
|
double rmax;
|
|
ae_vector tags;
|
|
ae_vector ctags;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t k2;
|
|
ae_int_t snnz;
|
|
ae_vector tmp0;
|
|
ae_vector tmp1;
|
|
ae_int_t layerscnt;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
_rbfreport_clear(rep);
|
|
_kdtree_init(&tree, _state, ae_true);
|
|
_kdtree_init(&ctree, _state, ae_true);
|
|
ae_vector_init(&dist, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xcx, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&omega, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&residualy, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&radius, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&xc, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&mnx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&mxx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&edge, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&mxsteps, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&ctags, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(s->nx==2||s->nx==3, "RBFBuildModel: S.NX<>2 or S.NX<>3!", _state);
|
|
|
|
/*
|
|
* Quick exit when we have no points
|
|
*/
|
|
if( s->n==0 )
|
|
{
|
|
rep->terminationtype = 1;
|
|
rep->iterationscount = 0;
|
|
rep->nmv = 0;
|
|
rep->arows = 0;
|
|
rep->acols = 0;
|
|
kdtreebuildtagged(&s->xc, &tags, 0, rbf_mxnx, 0, 2, &s->tree, _state);
|
|
ae_matrix_set_length(&s->xc, 0, 0, _state);
|
|
ae_matrix_set_length(&s->wr, 0, 0, _state);
|
|
s->nc = 0;
|
|
s->rmax = 0;
|
|
ae_matrix_set_length(&s->v, s->ny, rbf_mxnx+1, _state);
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = 0;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* General case, N>0
|
|
*/
|
|
rep->annz = 0;
|
|
rep->iterationscount = 0;
|
|
rep->nmv = 0;
|
|
ae_vector_set_length(&xcx, rbf_mxnx, _state);
|
|
|
|
/*
|
|
* First model in a sequence - linear model.
|
|
* Residuals from linear regression are stored in the ResidualY variable
|
|
* (used later to build RBF models).
|
|
*/
|
|
ae_matrix_set_length(&residualy, s->n, s->ny, _state);
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
for(j=0; j<=s->ny-1; j++)
|
|
{
|
|
residualy.ptr.pp_double[i][j] = s->y.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
if( !rbf_buildlinearmodel(&s->x, &residualy, s->n, s->ny, s->aterm, &v, _state) )
|
|
{
|
|
rep->terminationtype = -5;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Handle special case: multilayer model with NLayers=0.
|
|
* Quick exit.
|
|
*/
|
|
if( s->algorithmtype==2&&s->nlayers==0 )
|
|
{
|
|
rep->terminationtype = 1;
|
|
rep->iterationscount = 0;
|
|
rep->nmv = 0;
|
|
rep->arows = 0;
|
|
rep->acols = 0;
|
|
kdtreebuildtagged(&s->xc, &tags, 0, rbf_mxnx, 0, 2, &s->tree, _state);
|
|
ae_matrix_set_length(&s->xc, 0, 0, _state);
|
|
ae_matrix_set_length(&s->wr, 0, 0, _state);
|
|
s->nc = 0;
|
|
s->rmax = 0;
|
|
ae_matrix_set_length(&s->v, s->ny, rbf_mxnx+1, _state);
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = v.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Second model in a sequence - RBF term.
|
|
*
|
|
* NOTE: assignments below are not necessary, but without them
|
|
* MSVC complains about unitialized variables.
|
|
*/
|
|
nc = 0;
|
|
rmax = 0;
|
|
layerscnt = 0;
|
|
if( s->algorithmtype==1 )
|
|
{
|
|
|
|
/*
|
|
* Add RBF model.
|
|
* This model uses local KD-trees to speed-up nearest neighbor searches.
|
|
*/
|
|
if( s->gridtype==1 )
|
|
{
|
|
ae_vector_set_length(&mxx, s->nx, _state);
|
|
ae_vector_set_length(&mnx, s->nx, _state);
|
|
ae_vector_set_length(&mxsteps, s->nx, _state);
|
|
ae_vector_set_length(&edge, s->nx, _state);
|
|
for(i=0; i<=s->nx-1; i++)
|
|
{
|
|
mxx.ptr.p_double[i] = s->x.ptr.pp_double[0][i];
|
|
mnx.ptr.p_double[i] = s->x.ptr.pp_double[0][i];
|
|
}
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
for(j=0; j<=s->nx-1; j++)
|
|
{
|
|
if( ae_fp_less(mxx.ptr.p_double[j],s->x.ptr.pp_double[i][j]) )
|
|
{
|
|
mxx.ptr.p_double[j] = s->x.ptr.pp_double[i][j];
|
|
}
|
|
if( ae_fp_greater(mnx.ptr.p_double[j],s->x.ptr.pp_double[i][j]) )
|
|
{
|
|
mnx.ptr.p_double[j] = s->x.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<=s->nx-1; i++)
|
|
{
|
|
mxsteps.ptr.p_int[i] = ae_trunc((mxx.ptr.p_double[i]-mnx.ptr.p_double[i])/(2*s->h), _state)+1;
|
|
edge.ptr.p_double[i] = (mxx.ptr.p_double[i]+mnx.ptr.p_double[i])/2-s->h*mxsteps.ptr.p_int[i];
|
|
}
|
|
nc = 1;
|
|
for(i=0; i<=s->nx-1; i++)
|
|
{
|
|
mxsteps.ptr.p_int[i] = 2*mxsteps.ptr.p_int[i]+1;
|
|
nc = nc*mxsteps.ptr.p_int[i];
|
|
}
|
|
ae_matrix_set_length(&xc, nc, rbf_mxnx, _state);
|
|
if( s->nx==2 )
|
|
{
|
|
for(i=0; i<=mxsteps.ptr.p_int[0]-1; i++)
|
|
{
|
|
for(j=0; j<=mxsteps.ptr.p_int[1]-1; j++)
|
|
{
|
|
for(k2=0; k2<=rbf_mxnx-1; k2++)
|
|
{
|
|
xc.ptr.pp_double[i*mxsteps.ptr.p_int[1]+j][k2] = 0;
|
|
}
|
|
xc.ptr.pp_double[i*mxsteps.ptr.p_int[1]+j][0] = edge.ptr.p_double[0]+s->h*i;
|
|
xc.ptr.pp_double[i*mxsteps.ptr.p_int[1]+j][1] = edge.ptr.p_double[1]+s->h*j;
|
|
}
|
|
}
|
|
}
|
|
if( s->nx==3 )
|
|
{
|
|
for(i=0; i<=mxsteps.ptr.p_int[0]-1; i++)
|
|
{
|
|
for(j=0; j<=mxsteps.ptr.p_int[1]-1; j++)
|
|
{
|
|
for(k=0; k<=mxsteps.ptr.p_int[2]-1; k++)
|
|
{
|
|
for(k2=0; k2<=rbf_mxnx-1; k2++)
|
|
{
|
|
xc.ptr.pp_double[i*mxsteps.ptr.p_int[1]+j][k2] = 0;
|
|
}
|
|
xc.ptr.pp_double[(i*mxsteps.ptr.p_int[1]+j)*mxsteps.ptr.p_int[2]+k][0] = edge.ptr.p_double[0]+s->h*i;
|
|
xc.ptr.pp_double[(i*mxsteps.ptr.p_int[1]+j)*mxsteps.ptr.p_int[2]+k][1] = edge.ptr.p_double[1]+s->h*j;
|
|
xc.ptr.pp_double[(i*mxsteps.ptr.p_int[1]+j)*mxsteps.ptr.p_int[2]+k][2] = edge.ptr.p_double[2]+s->h*k;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( s->gridtype==2 )
|
|
{
|
|
nc = s->n;
|
|
ae_matrix_set_length(&xc, nc, rbf_mxnx, _state);
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xc.ptr.pp_double[i][j] = s->x.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( s->gridtype==3 )
|
|
{
|
|
nc = s->nc;
|
|
ae_matrix_set_length(&xc, nc, rbf_mxnx, _state);
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xc.ptr.pp_double[i][j] = s->xc.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ae_assert(ae_false, "RBFBuildModel: either S.GridType<1 or S.GridType>3!", _state);
|
|
}
|
|
}
|
|
}
|
|
rmax = 0;
|
|
ae_vector_set_length(&radius, nc, _state);
|
|
ae_vector_set_length(&ctags, nc, _state);
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
ctags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(&xc, &ctags, nc, rbf_mxnx, 0, 2, &ctree, _state);
|
|
if( s->fixrad )
|
|
{
|
|
|
|
/*
|
|
* Fixed radius
|
|
*/
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
radius.ptr.p_double[i] = s->radvalue;
|
|
}
|
|
rmax = radius.ptr.p_double[0];
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Dynamic radius
|
|
*/
|
|
if( nc==0 )
|
|
{
|
|
rmax = 1;
|
|
}
|
|
else
|
|
{
|
|
if( nc==1 )
|
|
{
|
|
radius.ptr.p_double[0] = s->radvalue;
|
|
rmax = radius.ptr.p_double[0];
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* NC>1, calculate radii using distances to nearest neigbors
|
|
*/
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xcx.ptr.p_double[j] = xc.ptr.pp_double[i][j];
|
|
}
|
|
if( kdtreequeryknn(&ctree, &xcx, 1, ae_false, _state)>0 )
|
|
{
|
|
kdtreequeryresultsdistances(&ctree, &dist, _state);
|
|
radius.ptr.p_double[i] = s->radvalue*dist.ptr.p_double[0];
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* No neighbors found (it will happen when we have only one center).
|
|
* Initialize radius with default value.
|
|
*/
|
|
radius.ptr.p_double[i] = 1.0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Apply filtering
|
|
*/
|
|
rvectorsetlengthatleast(&tmp0, nc, _state);
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
tmp0.ptr.p_double[i] = radius.ptr.p_double[i];
|
|
}
|
|
tagsortfast(&tmp0, &tmp1, nc, _state);
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
radius.ptr.p_double[i] = ae_minreal(radius.ptr.p_double[i], s->radzvalue*tmp0.ptr.p_double[nc/2], _state);
|
|
}
|
|
|
|
/*
|
|
* Calculate RMax, check that all radii are non-zero
|
|
*/
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
rmax = ae_maxreal(rmax, radius.ptr.p_double[i], _state);
|
|
}
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
if( ae_fp_eq(radius.ptr.p_double[i],0) )
|
|
{
|
|
rep->terminationtype = -5;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ivectorsetlengthatleast(&tags, s->n, _state);
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
tags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(&s->x, &tags, s->n, rbf_mxnx, 0, 2, &tree, _state);
|
|
rbf_buildrbfmodellsqr(&s->x, &residualy, &xc, &radius, s->n, nc, s->ny, &tree, &ctree, s->epsort, s->epserr, s->maxits, &rep->annz, &snnz, &omega, &rep->terminationtype, &rep->iterationscount, &rep->nmv, _state);
|
|
layerscnt = 1;
|
|
}
|
|
else
|
|
{
|
|
if( s->algorithmtype==2 )
|
|
{
|
|
rmax = s->radvalue;
|
|
rbf_buildrbfmlayersmodellsqr(&s->x, &residualy, &xc, s->radvalue, &radius, s->n, &nc, s->ny, s->nlayers, &ctree, 1.0E-6, 1.0E-6, 50, s->lambdav, &rep->annz, &omega, &rep->terminationtype, &rep->iterationscount, &rep->nmv, _state);
|
|
layerscnt = s->nlayers;
|
|
}
|
|
else
|
|
{
|
|
ae_assert(ae_false, "RBFBuildModel: internal error(AlgorithmType neither 1 nor 2)", _state);
|
|
}
|
|
}
|
|
if( rep->terminationtype<=0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Model is built
|
|
*/
|
|
s->nc = nc/layerscnt;
|
|
s->rmax = rmax;
|
|
s->nl = layerscnt;
|
|
ae_matrix_set_length(&s->xc, s->nc, rbf_mxnx, _state);
|
|
ae_matrix_set_length(&s->wr, s->nc, 1+s->nl*s->ny, _state);
|
|
ae_matrix_set_length(&s->v, s->ny, rbf_mxnx+1, _state);
|
|
for(i=0; i<=s->nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
s->xc.ptr.pp_double[i][j] = xc.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
ivectorsetlengthatleast(&tags, s->nc, _state);
|
|
for(i=0; i<=s->nc-1; i++)
|
|
{
|
|
tags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(&s->xc, &tags, s->nc, rbf_mxnx, 0, 2, &s->tree, _state);
|
|
for(i=0; i<=s->nc-1; i++)
|
|
{
|
|
s->wr.ptr.pp_double[i][0] = radius.ptr.p_double[i];
|
|
for(k=0; k<=layerscnt-1; k++)
|
|
{
|
|
for(j=0; j<=s->ny-1; j++)
|
|
{
|
|
s->wr.ptr.pp_double[i][1+k*s->ny+j] = omega.ptr.pp_double[k*s->nc+i][j];
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = v.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
rep->terminationtype = 1;
|
|
rep->arows = s->n;
|
|
rep->acols = s->nc;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model in the given point.
|
|
|
|
This function should be used when we have NY=1 (scalar function) and NX=2
|
|
(2-dimensional space). If you have 3-dimensional space, use RBFCalc3(). If
|
|
you have general situation (NX-dimensional space, NY-dimensional function)
|
|
you should use general, less efficient implementation RBFCalc().
|
|
|
|
If you want to calculate function values many times, consider using
|
|
RBFGridCalc2(), which is far more efficient than many subsequent calls to
|
|
RBFCalc2().
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
*NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - first coordinate, finite number
|
|
X1 - second coordinate, finite number
|
|
|
|
RESULT:
|
|
value of the model or 0.0 (as defined above)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfcalc2(rbfmodel* s, double x0, double x1, ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t lx;
|
|
ae_int_t tg;
|
|
double d2;
|
|
double t;
|
|
double bfcur;
|
|
double rcur;
|
|
double result;
|
|
|
|
|
|
ae_assert(ae_isfinite(x0, _state), "RBFCalc2: invalid value for X0 (X0 is Inf)!", _state);
|
|
ae_assert(ae_isfinite(x1, _state), "RBFCalc2: invalid value for X1 (X1 is Inf)!", _state);
|
|
if( s->ny!=1||s->nx!=2 )
|
|
{
|
|
result = 0;
|
|
return result;
|
|
}
|
|
result = s->v.ptr.pp_double[0][0]*x0+s->v.ptr.pp_double[0][1]*x1+s->v.ptr.pp_double[0][rbf_mxnx];
|
|
if( s->nc==0 )
|
|
{
|
|
return result;
|
|
}
|
|
rvectorsetlengthatleast(&s->calcbufxcx, rbf_mxnx, _state);
|
|
for(i=0; i<=rbf_mxnx-1; i++)
|
|
{
|
|
s->calcbufxcx.ptr.p_double[i] = 0.0;
|
|
}
|
|
s->calcbufxcx.ptr.p_double[0] = x0;
|
|
s->calcbufxcx.ptr.p_double[1] = x1;
|
|
lx = kdtreequeryrnn(&s->tree, &s->calcbufxcx, s->rmax*rbf_rbffarradius, ae_true, _state);
|
|
kdtreequeryresultsx(&s->tree, &s->calcbufx, _state);
|
|
kdtreequeryresultstags(&s->tree, &s->calcbuftags, _state);
|
|
for(i=0; i<=lx-1; i++)
|
|
{
|
|
tg = s->calcbuftags.ptr.p_int[i];
|
|
d2 = ae_sqr(x0-s->calcbufx.ptr.pp_double[i][0], _state)+ae_sqr(x1-s->calcbufx.ptr.pp_double[i][1], _state);
|
|
rcur = s->wr.ptr.pp_double[tg][0];
|
|
bfcur = ae_exp(-d2/(rcur*rcur), _state);
|
|
for(j=0; j<=s->nl-1; j++)
|
|
{
|
|
result = result+bfcur*s->wr.ptr.pp_double[tg][1+j];
|
|
rcur = 0.5*rcur;
|
|
t = bfcur*bfcur;
|
|
bfcur = t*t;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model in the given point.
|
|
|
|
This function should be used when we have NY=1 (scalar function) and NX=3
|
|
(3-dimensional space). If you have 2-dimensional space, use RBFCalc2(). If
|
|
you have general situation (NX-dimensional space, NY-dimensional function)
|
|
you should use general, less efficient implementation RBFCalc().
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>3
|
|
*NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - first coordinate, finite number
|
|
X1 - second coordinate, finite number
|
|
X2 - third coordinate, finite number
|
|
|
|
RESULT:
|
|
value of the model or 0.0 (as defined above)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfcalc3(rbfmodel* s,
|
|
double x0,
|
|
double x1,
|
|
double x2,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t lx;
|
|
ae_int_t tg;
|
|
double t;
|
|
double rcur;
|
|
double bf;
|
|
double result;
|
|
|
|
|
|
ae_assert(ae_isfinite(x0, _state), "RBFCalc3: invalid value for X0 (X0 is Inf or NaN)!", _state);
|
|
ae_assert(ae_isfinite(x1, _state), "RBFCalc3: invalid value for X1 (X1 is Inf or NaN)!", _state);
|
|
ae_assert(ae_isfinite(x2, _state), "RBFCalc3: invalid value for X2 (X2 is Inf or NaN)!", _state);
|
|
if( s->ny!=1||s->nx!=3 )
|
|
{
|
|
result = 0;
|
|
return result;
|
|
}
|
|
result = s->v.ptr.pp_double[0][0]*x0+s->v.ptr.pp_double[0][1]*x1+s->v.ptr.pp_double[0][2]*x2+s->v.ptr.pp_double[0][rbf_mxnx];
|
|
if( s->nc==0 )
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* calculating value for F(X)
|
|
*/
|
|
rvectorsetlengthatleast(&s->calcbufxcx, rbf_mxnx, _state);
|
|
for(i=0; i<=rbf_mxnx-1; i++)
|
|
{
|
|
s->calcbufxcx.ptr.p_double[i] = 0.0;
|
|
}
|
|
s->calcbufxcx.ptr.p_double[0] = x0;
|
|
s->calcbufxcx.ptr.p_double[1] = x1;
|
|
s->calcbufxcx.ptr.p_double[2] = x2;
|
|
lx = kdtreequeryrnn(&s->tree, &s->calcbufxcx, s->rmax*rbf_rbffarradius, ae_true, _state);
|
|
kdtreequeryresultsx(&s->tree, &s->calcbufx, _state);
|
|
kdtreequeryresultstags(&s->tree, &s->calcbuftags, _state);
|
|
for(i=0; i<=lx-1; i++)
|
|
{
|
|
tg = s->calcbuftags.ptr.p_int[i];
|
|
rcur = s->wr.ptr.pp_double[tg][0];
|
|
bf = ae_exp(-(ae_sqr(x0-s->calcbufx.ptr.pp_double[i][0], _state)+ae_sqr(x1-s->calcbufx.ptr.pp_double[i][1], _state)+ae_sqr(x2-s->calcbufx.ptr.pp_double[i][2], _state))/ae_sqr(rcur, _state), _state);
|
|
for(j=0; j<=s->nl-1; j++)
|
|
{
|
|
result = result+bf*s->wr.ptr.pp_double[tg][1+j];
|
|
t = bf*bf;
|
|
bf = t*t;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the given point.
|
|
|
|
This is general function which can be used for arbitrary NX (dimension of
|
|
the space of arguments) and NY (dimension of the function itself). However
|
|
when you have NY=1 you may find more convenient to use RBFCalc2() or
|
|
RBFCalc3().
|
|
|
|
This function returns 0.0 when model is not initialized.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X - coordinates, array[NX].
|
|
X may have more than NX elements, in this case only
|
|
leading NX will be used.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function value, array[NY]. Y is out-parameter and
|
|
reallocated after call to this function. In case you want
|
|
to reuse previously allocated Y, you may use RBFCalcBuf(),
|
|
which reallocates Y only when it is too small.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcalc(rbfmodel* s,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
|
|
ae_vector_clear(y);
|
|
|
|
ae_assert(x->cnt>=s->nx, "RBFCalc: Length(X)<NX", _state);
|
|
ae_assert(isfinitevector(x, s->nx, _state), "RBFCalc: X contains infinite or NaN values", _state);
|
|
rbfcalcbuf(s, x, y, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the given point.
|
|
|
|
Same as RBFCalc(), but does not reallocate Y when in is large enough to
|
|
store function values.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X - coordinates, array[NX].
|
|
X may have more than NX elements, in this case only
|
|
leading NX will be used.
|
|
Y - possibly preallocated array
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function value, array[NY]. Y is not reallocated when it
|
|
is larger than NY.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcalcbuf(rbfmodel* s,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t lx;
|
|
ae_int_t tg;
|
|
double t;
|
|
double rcur;
|
|
double bf;
|
|
|
|
|
|
ae_assert(x->cnt>=s->nx, "RBFCalcBuf: Length(X)<NX", _state);
|
|
ae_assert(isfinitevector(x, s->nx, _state), "RBFCalcBuf: X contains infinite or NaN values", _state);
|
|
if( y->cnt<s->ny )
|
|
{
|
|
ae_vector_set_length(y, s->ny, _state);
|
|
}
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
y->ptr.p_double[i] = s->v.ptr.pp_double[i][rbf_mxnx];
|
|
for(j=0; j<=s->nx-1; j++)
|
|
{
|
|
y->ptr.p_double[i] = y->ptr.p_double[i]+s->v.ptr.pp_double[i][j]*x->ptr.p_double[j];
|
|
}
|
|
}
|
|
if( s->nc==0 )
|
|
{
|
|
return;
|
|
}
|
|
rvectorsetlengthatleast(&s->calcbufxcx, rbf_mxnx, _state);
|
|
for(i=0; i<=rbf_mxnx-1; i++)
|
|
{
|
|
s->calcbufxcx.ptr.p_double[i] = 0.0;
|
|
}
|
|
for(i=0; i<=s->nx-1; i++)
|
|
{
|
|
s->calcbufxcx.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
lx = kdtreequeryrnn(&s->tree, &s->calcbufxcx, s->rmax*rbf_rbffarradius, ae_true, _state);
|
|
kdtreequeryresultsx(&s->tree, &s->calcbufx, _state);
|
|
kdtreequeryresultstags(&s->tree, &s->calcbuftags, _state);
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
for(j=0; j<=lx-1; j++)
|
|
{
|
|
tg = s->calcbuftags.ptr.p_int[j];
|
|
rcur = s->wr.ptr.pp_double[tg][0];
|
|
bf = ae_exp(-(ae_sqr(s->calcbufxcx.ptr.p_double[0]-s->calcbufx.ptr.pp_double[j][0], _state)+ae_sqr(s->calcbufxcx.ptr.p_double[1]-s->calcbufx.ptr.pp_double[j][1], _state)+ae_sqr(s->calcbufxcx.ptr.p_double[2]-s->calcbufx.ptr.pp_double[j][2], _state))/ae_sqr(rcur, _state), _state);
|
|
for(k=0; k<=s->nl-1; k++)
|
|
{
|
|
y->ptr.p_double[i] = y->ptr.p_double[i]+bf*s->wr.ptr.pp_double[tg][1+k*s->ny+i];
|
|
t = bf*bf;
|
|
bf = t*t;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the regular grid.
|
|
|
|
Grid have N0*N1 points, with Point[I,J] = (X0[I], X1[J])
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
*NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - array of grid nodes, first coordinates, array[N0]
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[N0,N1]. Y is out-variable and
|
|
is reallocated by this function.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc2(rbfmodel* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_matrix* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector cpx0;
|
|
ae_vector cpx1;
|
|
ae_vector p01;
|
|
ae_vector p11;
|
|
ae_vector p2;
|
|
double rlimit;
|
|
double xcnorm2;
|
|
ae_int_t hp01;
|
|
double hcpx0;
|
|
double xc0;
|
|
double xc1;
|
|
double omega;
|
|
double radius;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t d;
|
|
ae_int_t i00;
|
|
ae_int_t i01;
|
|
ae_int_t i10;
|
|
ae_int_t i11;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_clear(y);
|
|
ae_vector_init(&cpx0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&cpx1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p01, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&p11, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&p2, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_assert(n0>0, "RBFGridCalc2: invalid value for N0 (N0<=0)!", _state);
|
|
ae_assert(n1>0, "RBFGridCalc2: invalid value for N1 (N1<=0)!", _state);
|
|
ae_assert(x0->cnt>=n0, "RBFGridCalc2: Length(X0)<N0", _state);
|
|
ae_assert(x1->cnt>=n1, "RBFGridCalc2: Length(X1)<N1", _state);
|
|
ae_assert(isfinitevector(x0, n0, _state), "RBFGridCalc2: X0 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x1, n1, _state), "RBFGridCalc2: X1 contains infinite or NaN values!", _state);
|
|
ae_matrix_set_length(y, n0, n1, _state);
|
|
for(i=0; i<=n0-1; i++)
|
|
{
|
|
for(j=0; j<=n1-1; j++)
|
|
{
|
|
y->ptr.pp_double[i][j] = 0;
|
|
}
|
|
}
|
|
if( (s->ny!=1||s->nx!=2)||s->nc==0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*create and sort arrays
|
|
*/
|
|
ae_vector_set_length(&cpx0, n0, _state);
|
|
for(i=0; i<=n0-1; i++)
|
|
{
|
|
cpx0.ptr.p_double[i] = x0->ptr.p_double[i];
|
|
}
|
|
tagsort(&cpx0, n0, &p01, &p2, _state);
|
|
ae_vector_set_length(&cpx1, n1, _state);
|
|
for(i=0; i<=n1-1; i++)
|
|
{
|
|
cpx1.ptr.p_double[i] = x1->ptr.p_double[i];
|
|
}
|
|
tagsort(&cpx1, n1, &p11, &p2, _state);
|
|
|
|
/*
|
|
*calculate function's value
|
|
*/
|
|
for(i=0; i<=s->nc-1; i++)
|
|
{
|
|
radius = s->wr.ptr.pp_double[i][0];
|
|
for(d=0; d<=s->nl-1; d++)
|
|
{
|
|
omega = s->wr.ptr.pp_double[i][1+d];
|
|
rlimit = radius*rbf_rbffarradius;
|
|
|
|
/*
|
|
*search lower and upper indexes
|
|
*/
|
|
i00 = lowerbound(&cpx0, n0, s->xc.ptr.pp_double[i][0]-rlimit, _state);
|
|
i01 = upperbound(&cpx0, n0, s->xc.ptr.pp_double[i][0]+rlimit, _state);
|
|
i10 = lowerbound(&cpx1, n1, s->xc.ptr.pp_double[i][1]-rlimit, _state);
|
|
i11 = upperbound(&cpx1, n1, s->xc.ptr.pp_double[i][1]+rlimit, _state);
|
|
xc0 = s->xc.ptr.pp_double[i][0];
|
|
xc1 = s->xc.ptr.pp_double[i][1];
|
|
for(j=i00; j<=i01-1; j++)
|
|
{
|
|
hcpx0 = cpx0.ptr.p_double[j];
|
|
hp01 = p01.ptr.p_int[j];
|
|
for(k=i10; k<=i11-1; k++)
|
|
{
|
|
xcnorm2 = ae_sqr(hcpx0-xc0, _state)+ae_sqr(cpx1.ptr.p_double[k]-xc1, _state);
|
|
if( ae_fp_less_eq(xcnorm2,rlimit*rlimit) )
|
|
{
|
|
y->ptr.pp_double[hp01][p11.ptr.p_int[k]] = y->ptr.pp_double[hp01][p11.ptr.p_int[k]]+ae_exp(-xcnorm2/ae_sqr(radius, _state), _state)*omega;
|
|
}
|
|
}
|
|
}
|
|
radius = 0.5*radius;
|
|
}
|
|
}
|
|
|
|
/*
|
|
*add linear term
|
|
*/
|
|
for(i=0; i<=n0-1; i++)
|
|
{
|
|
for(j=0; j<=n1-1; j++)
|
|
{
|
|
y->ptr.pp_double[i][j] = y->ptr.pp_double[i][j]+s->v.ptr.pp_double[0][0]*x0->ptr.p_double[i]+s->v.ptr.pp_double[0][1]*x1->ptr.p_double[j]+s->v.ptr.pp_double[0][rbf_mxnx];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function "unpacks" RBF model by extracting its coefficients.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
|
|
OUTPUT PARAMETERS:
|
|
NX - dimensionality of argument
|
|
NY - dimensionality of the target function
|
|
XWR - model information, array[NC,NX+NY+1].
|
|
One row of the array corresponds to one basis function:
|
|
* first NX columns - coordinates of the center
|
|
* next NY columns - weights, one per dimension of the
|
|
function being modelled
|
|
* last column - radius, same for all dimensions of
|
|
the function being modelled
|
|
NC - number of the centers
|
|
V - polynomial term , array[NY,NX+1]. One row per one
|
|
dimension of the function being modelled. First NX
|
|
elements are linear coefficients, V[NX] is equal to the
|
|
constant part.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfunpack(rbfmodel* s,
|
|
ae_int_t* nx,
|
|
ae_int_t* ny,
|
|
/* Real */ ae_matrix* xwr,
|
|
ae_int_t* nc,
|
|
/* Real */ ae_matrix* v,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double rcur;
|
|
|
|
*nx = 0;
|
|
*ny = 0;
|
|
ae_matrix_clear(xwr);
|
|
*nc = 0;
|
|
ae_matrix_clear(v);
|
|
|
|
*nx = s->nx;
|
|
*ny = s->ny;
|
|
*nc = s->nc;
|
|
|
|
/*
|
|
* Fill V
|
|
*/
|
|
ae_matrix_set_length(v, s->ny, s->nx+1, _state);
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
ae_v_move(&v->ptr.pp_double[i][0], 1, &s->v.ptr.pp_double[i][0], 1, ae_v_len(0,s->nx-1));
|
|
v->ptr.pp_double[i][s->nx] = s->v.ptr.pp_double[i][rbf_mxnx];
|
|
}
|
|
|
|
/*
|
|
* Fill XWR and V
|
|
*/
|
|
if( *nc*s->nl>0 )
|
|
{
|
|
ae_matrix_set_length(xwr, s->nc*s->nl, s->nx+s->ny+1, _state);
|
|
for(i=0; i<=s->nc-1; i++)
|
|
{
|
|
rcur = s->wr.ptr.pp_double[i][0];
|
|
for(j=0; j<=s->nl-1; j++)
|
|
{
|
|
ae_v_move(&xwr->ptr.pp_double[i*s->nl+j][0], 1, &s->xc.ptr.pp_double[i][0], 1, ae_v_len(0,s->nx-1));
|
|
ae_v_move(&xwr->ptr.pp_double[i*s->nl+j][s->nx], 1, &s->wr.ptr.pp_double[i][1+j*s->ny], 1, ae_v_len(s->nx,s->nx+s->ny-1));
|
|
xwr->ptr.pp_double[i*s->nl+j][s->nx+s->ny] = rcur;
|
|
rcur = 0.5*rcur;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: allocation
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.02.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfalloc(ae_serializer* s, rbfmodel* model, ae_state *_state)
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
|
|
/*
|
|
* Data
|
|
*/
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
kdtreealloc(s, &model->tree, _state);
|
|
allocrealmatrix(s, &model->xc, -1, -1, _state);
|
|
allocrealmatrix(s, &model->wr, -1, -1, _state);
|
|
ae_serializer_alloc_entry(s);
|
|
allocrealmatrix(s, &model->v, -1, -1, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: serialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.02.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfserialize(ae_serializer* s, rbfmodel* model, ae_state *_state)
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_serialize_int(s, getrbfserializationcode(_state), _state);
|
|
ae_serializer_serialize_int(s, rbf_rbffirstversion, _state);
|
|
|
|
/*
|
|
* Data
|
|
*/
|
|
ae_serializer_serialize_int(s, model->nx, _state);
|
|
ae_serializer_serialize_int(s, model->ny, _state);
|
|
ae_serializer_serialize_int(s, model->nc, _state);
|
|
ae_serializer_serialize_int(s, model->nl, _state);
|
|
kdtreeserialize(s, &model->tree, _state);
|
|
serializerealmatrix(s, &model->xc, -1, -1, _state);
|
|
serializerealmatrix(s, &model->wr, -1, -1, _state);
|
|
ae_serializer_serialize_double(s, model->rmax, _state);
|
|
serializerealmatrix(s, &model->v, -1, -1, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: unserialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.02.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfunserialize(ae_serializer* s, rbfmodel* model, ae_state *_state)
|
|
{
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
|
|
_rbfmodel_clear(model);
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_unserialize_int(s, &i0, _state);
|
|
ae_assert(i0==getrbfserializationcode(_state), "RBFUnserialize: stream header corrupted", _state);
|
|
ae_serializer_unserialize_int(s, &i1, _state);
|
|
ae_assert(i1==rbf_rbffirstversion, "RBFUnserialize: stream header corrupted", _state);
|
|
|
|
/*
|
|
* Unserialize primary model parameters, initialize model.
|
|
*
|
|
* It is necessary to call RBFCreate() because some internal fields
|
|
* which are NOT unserialized will need initialization.
|
|
*/
|
|
ae_serializer_unserialize_int(s, &nx, _state);
|
|
ae_serializer_unserialize_int(s, &ny, _state);
|
|
rbfcreate(nx, ny, model, _state);
|
|
ae_serializer_unserialize_int(s, &model->nc, _state);
|
|
ae_serializer_unserialize_int(s, &model->nl, _state);
|
|
kdtreeunserialize(s, &model->tree, _state);
|
|
unserializerealmatrix(s, &model->xc, _state);
|
|
unserializerealmatrix(s, &model->wr, _state);
|
|
ae_serializer_unserialize_double(s, &model->rmax, _state);
|
|
unserializerealmatrix(s, &model->v, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function changes centers allocation algorithm to one which allocates
|
|
centers exactly at the dataset points (one input point = one center). This
|
|
function won't have effect until next call to RBFBuildModel().
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbf_rbfgridpoints(rbfmodel* s, ae_state *_state)
|
|
{
|
|
|
|
|
|
s->gridtype = 2;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function changes radii calculation algorithm to one which makes
|
|
radius for I-th node equal to R[i]=DistNN[i]*Q, where:
|
|
* R[i] is a radius calculated by the algorithm
|
|
* DistNN[i] is distance from I-th center to its nearest neighbor center
|
|
* Q is a scale parameter, which should be within [0.75,1.50], with
|
|
recommended value equal to 1.0
|
|
* after performing radii calculation, radii are transformed in order to
|
|
avoid situation when single outlier has very large radius and influences
|
|
many points across entire dataset. Transformation has following form:
|
|
new_r[i] = min(r[i],Z*median(r[]))
|
|
where r[i] is I-th radius, median() is a median radius across entire
|
|
dataset, Z is user-specified value which controls amount of deviation
|
|
from median radius.
|
|
|
|
This function won't have effect until next call to RBFBuildModel().
|
|
|
|
The idea behind this algorithm is to choose radii corresponding to basis
|
|
functions is such way that I-th radius is approximately equal to distance
|
|
from I-th center to its nearest neighbor. In this case interactions with
|
|
distant points will be insignificant, and we will get well conditioned
|
|
basis.
|
|
|
|
Properties of this basis depend on the value of Q:
|
|
* Q<0.75 will give perfectly conditioned basis, but terrible smoothness
|
|
properties (RBF interpolant will have sharp peaks around function values)
|
|
* Q>1.5 will lead to badly conditioned systems and slow convergence of the
|
|
underlying linear solver (although smoothness will be very good)
|
|
* Q around 1.0 gives good balance between smoothness and condition number
|
|
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
Q - radius coefficient, Q>0
|
|
Z - z-parameter, Z>0
|
|
|
|
Default value of Q is equal to 1.0
|
|
Default value of Z is equal to 5.0
|
|
|
|
NOTE: this function has some serialization-related subtleties. We
|
|
recommend you to study serialization examples from ALGLIB Reference
|
|
Manual if you want to perform serialization of your models.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbf_rbfradnn(rbfmodel* s,
|
|
double q,
|
|
double z,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(q, _state)&&ae_fp_greater(q,0), "RBFRadNN: Q<=0, infinite or NAN", _state);
|
|
ae_assert(ae_isfinite(z, _state)&&ae_fp_greater(z,0), "RBFRadNN: Z<=0, infinite or NAN", _state);
|
|
s->fixrad = ae_false;
|
|
s->radvalue = q;
|
|
s->radzvalue = z;
|
|
}
|
|
|
|
|
|
static ae_bool rbf_buildlinearmodel(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
ae_int_t n,
|
|
ae_int_t ny,
|
|
ae_int_t modeltype,
|
|
/* Real */ ae_matrix* v,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector tmpy;
|
|
ae_matrix a;
|
|
double scaling;
|
|
ae_vector shifting;
|
|
double mn;
|
|
double mx;
|
|
ae_vector c;
|
|
lsfitreport rep;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t info;
|
|
ae_bool result;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_clear(v);
|
|
ae_vector_init(&tmpy, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&shifting, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
|
|
_lsfitreport_init(&rep, _state, ae_true);
|
|
|
|
ae_assert(n>=0, "BuildLinearModel: N<0", _state);
|
|
ae_assert(ny>0, "BuildLinearModel: NY<=0", _state);
|
|
|
|
/*
|
|
* Handle degenerate case (N=0)
|
|
*/
|
|
result = ae_true;
|
|
ae_matrix_set_length(v, ny, rbf_mxnx+1, _state);
|
|
if( n==0 )
|
|
{
|
|
for(j=0; j<=rbf_mxnx; j++)
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
v->ptr.pp_double[i][j] = 0;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Allocate temporaries
|
|
*/
|
|
ae_vector_set_length(&tmpy, n, _state);
|
|
|
|
/*
|
|
* General linear model.
|
|
*/
|
|
if( modeltype==1 )
|
|
{
|
|
|
|
/*
|
|
* Calculate scaling/shifting, transform variables, prepare LLS problem
|
|
*/
|
|
ae_matrix_set_length(&a, n, rbf_mxnx+1, _state);
|
|
ae_vector_set_length(&shifting, rbf_mxnx, _state);
|
|
scaling = 0;
|
|
for(i=0; i<=rbf_mxnx-1; i++)
|
|
{
|
|
mn = x->ptr.pp_double[0][i];
|
|
mx = mn;
|
|
for(j=1; j<=n-1; j++)
|
|
{
|
|
if( ae_fp_greater(mn,x->ptr.pp_double[j][i]) )
|
|
{
|
|
mn = x->ptr.pp_double[j][i];
|
|
}
|
|
if( ae_fp_less(mx,x->ptr.pp_double[j][i]) )
|
|
{
|
|
mx = x->ptr.pp_double[j][i];
|
|
}
|
|
}
|
|
scaling = ae_maxreal(scaling, mx-mn, _state);
|
|
shifting.ptr.p_double[i] = 0.5*(mx+mn);
|
|
}
|
|
if( ae_fp_eq(scaling,0) )
|
|
{
|
|
scaling = 1;
|
|
}
|
|
else
|
|
{
|
|
scaling = 0.5*scaling;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
a.ptr.pp_double[i][j] = (x->ptr.pp_double[i][j]-shifting.ptr.p_double[j])/scaling;
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
a.ptr.pp_double[i][rbf_mxnx] = 1;
|
|
}
|
|
|
|
/*
|
|
* Solve linear system in transformed variables, make backward
|
|
*/
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
tmpy.ptr.p_double[j] = y->ptr.pp_double[j][i];
|
|
}
|
|
lsfitlinear(&tmpy, &a, n, rbf_mxnx+1, &info, &c, &rep, _state);
|
|
if( info<=0 )
|
|
{
|
|
result = ae_false;
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = c.ptr.p_double[j]/scaling;
|
|
}
|
|
v->ptr.pp_double[i][rbf_mxnx] = c.ptr.p_double[rbf_mxnx];
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][rbf_mxnx] = v->ptr.pp_double[i][rbf_mxnx]-shifting.ptr.p_double[j]*v->ptr.pp_double[i][j];
|
|
}
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
for(k=0; k<=rbf_mxnx-1; k++)
|
|
{
|
|
y->ptr.pp_double[j][i] = y->ptr.pp_double[j][i]-x->ptr.pp_double[j][k]*v->ptr.pp_double[i][k];
|
|
}
|
|
y->ptr.pp_double[j][i] = y->ptr.pp_double[j][i]-v->ptr.pp_double[i][rbf_mxnx];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Constant model, very simple
|
|
*/
|
|
if( modeltype==2 )
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = 0;
|
|
}
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][rbf_mxnx] = v->ptr.pp_double[i][rbf_mxnx]+y->ptr.pp_double[j][i];
|
|
}
|
|
if( n>0 )
|
|
{
|
|
v->ptr.pp_double[i][rbf_mxnx] = v->ptr.pp_double[i][rbf_mxnx]/n;
|
|
}
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
y->ptr.pp_double[j][i] = y->ptr.pp_double[j][i]-v->ptr.pp_double[i][rbf_mxnx];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Zero model
|
|
*/
|
|
ae_assert(modeltype==3, "BuildLinearModel: unknown model type", _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = 0;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
|
|
static void rbf_buildrbfmodellsqr(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
/* Real */ ae_matrix* xc,
|
|
/* Real */ ae_vector* r,
|
|
ae_int_t n,
|
|
ae_int_t nc,
|
|
ae_int_t ny,
|
|
kdtree* pointstree,
|
|
kdtree* centerstree,
|
|
double epsort,
|
|
double epserr,
|
|
ae_int_t maxits,
|
|
ae_int_t* gnnz,
|
|
ae_int_t* snnz,
|
|
/* Real */ ae_matrix* w,
|
|
ae_int_t* info,
|
|
ae_int_t* iterationscount,
|
|
ae_int_t* nmv,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
linlsqrstate state;
|
|
linlsqrreport lsqrrep;
|
|
sparsematrix spg;
|
|
sparsematrix sps;
|
|
ae_vector nearcenterscnt;
|
|
ae_vector nearpointscnt;
|
|
ae_vector skipnearpointscnt;
|
|
ae_vector farpointscnt;
|
|
ae_int_t maxnearcenterscnt;
|
|
ae_int_t maxnearpointscnt;
|
|
ae_int_t maxfarpointscnt;
|
|
ae_int_t sumnearcenterscnt;
|
|
ae_int_t sumnearpointscnt;
|
|
ae_int_t sumfarpointscnt;
|
|
double maxrad;
|
|
ae_vector pointstags;
|
|
ae_vector centerstags;
|
|
ae_matrix nearpoints;
|
|
ae_matrix nearcenters;
|
|
ae_matrix farpoints;
|
|
ae_int_t tmpi;
|
|
ae_int_t pointscnt;
|
|
ae_int_t centerscnt;
|
|
ae_vector xcx;
|
|
ae_vector tmpy;
|
|
ae_vector tc;
|
|
ae_vector g;
|
|
ae_vector c;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t sind;
|
|
ae_matrix a;
|
|
double vv;
|
|
double vx;
|
|
double vy;
|
|
double vz;
|
|
double vr;
|
|
double gnorm2;
|
|
ae_vector tmp0;
|
|
ae_vector tmp1;
|
|
ae_vector tmp2;
|
|
double fx;
|
|
ae_matrix xx;
|
|
ae_matrix cx;
|
|
double mrad;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
*gnnz = 0;
|
|
*snnz = 0;
|
|
ae_matrix_clear(w);
|
|
*info = 0;
|
|
*iterationscount = 0;
|
|
*nmv = 0;
|
|
_linlsqrstate_init(&state, _state, ae_true);
|
|
_linlsqrreport_init(&lsqrrep, _state, ae_true);
|
|
_sparsematrix_init(&spg, _state, ae_true);
|
|
_sparsematrix_init(&sps, _state, ae_true);
|
|
ae_vector_init(&nearcenterscnt, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&nearpointscnt, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&skipnearpointscnt, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&farpointscnt, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&pointstags, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(¢erstags, 0, DT_INT, _state, ae_true);
|
|
ae_matrix_init(&nearpoints, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&nearcenters, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&farpoints, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xcx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpy, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tc, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&g, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&c, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&a, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp2, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&xx, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&cx, 0, 0, DT_REAL, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Handle special cases: NC=0
|
|
*/
|
|
if( nc==0 )
|
|
{
|
|
*info = 1;
|
|
*iterationscount = 0;
|
|
*nmv = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Prepare for general case, NC>0
|
|
*/
|
|
ae_vector_set_length(&xcx, rbf_mxnx, _state);
|
|
ae_vector_set_length(&pointstags, n, _state);
|
|
ae_vector_set_length(¢erstags, nc, _state);
|
|
*info = -1;
|
|
*iterationscount = 0;
|
|
*nmv = 0;
|
|
|
|
/*
|
|
* This block prepares quantities used to compute approximate cardinal basis functions (ACBFs):
|
|
* * NearCentersCnt[] - array[NC], whose elements store number of near centers used to build ACBF
|
|
* * NearPointsCnt[] - array[NC], number of near points used to build ACBF
|
|
* * FarPointsCnt[] - array[NC], number of far points (ones where ACBF is nonzero)
|
|
* * MaxNearCentersCnt - max(NearCentersCnt)
|
|
* * MaxNearPointsCnt - max(NearPointsCnt)
|
|
* * SumNearCentersCnt - sum(NearCentersCnt)
|
|
* * SumNearPointsCnt - sum(NearPointsCnt)
|
|
* * SumFarPointsCnt - sum(FarPointsCnt)
|
|
*/
|
|
ae_vector_set_length(&nearcenterscnt, nc, _state);
|
|
ae_vector_set_length(&nearpointscnt, nc, _state);
|
|
ae_vector_set_length(&skipnearpointscnt, nc, _state);
|
|
ae_vector_set_length(&farpointscnt, nc, _state);
|
|
maxnearcenterscnt = 0;
|
|
maxnearpointscnt = 0;
|
|
maxfarpointscnt = 0;
|
|
sumnearcenterscnt = 0;
|
|
sumnearpointscnt = 0;
|
|
sumfarpointscnt = 0;
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xcx.ptr.p_double[j] = xc->ptr.pp_double[i][j];
|
|
}
|
|
|
|
/*
|
|
* Determine number of near centers and maximum radius of near centers
|
|
*/
|
|
nearcenterscnt.ptr.p_int[i] = kdtreequeryrnn(centerstree, &xcx, r->ptr.p_double[i]*rbf_rbfnearradius, ae_true, _state);
|
|
kdtreequeryresultstags(centerstree, ¢erstags, _state);
|
|
maxrad = 0;
|
|
for(j=0; j<=nearcenterscnt.ptr.p_int[i]-1; j++)
|
|
{
|
|
maxrad = ae_maxreal(maxrad, ae_fabs(r->ptr.p_double[centerstags.ptr.p_int[j]], _state), _state);
|
|
}
|
|
|
|
/*
|
|
* Determine number of near points (ones which used to build ACBF)
|
|
* and skipped points (the most near points which are NOT used to build ACBF
|
|
* and are NOT included in the near points count
|
|
*/
|
|
skipnearpointscnt.ptr.p_int[i] = kdtreequeryrnn(pointstree, &xcx, 0.1*r->ptr.p_double[i], ae_true, _state);
|
|
nearpointscnt.ptr.p_int[i] = kdtreequeryrnn(pointstree, &xcx, (r->ptr.p_double[i]+maxrad)*rbf_rbfnearradius, ae_true, _state)-skipnearpointscnt.ptr.p_int[i];
|
|
ae_assert(nearpointscnt.ptr.p_int[i]>=0, "BuildRBFModelLSQR: internal error", _state);
|
|
|
|
/*
|
|
* Determine number of far points
|
|
*/
|
|
farpointscnt.ptr.p_int[i] = kdtreequeryrnn(pointstree, &xcx, ae_maxreal(r->ptr.p_double[i]*rbf_rbfnearradius+maxrad*rbf_rbffarradius, r->ptr.p_double[i]*rbf_rbffarradius, _state), ae_true, _state);
|
|
|
|
/*
|
|
* calculate sum and max, make some basic checks
|
|
*/
|
|
ae_assert(nearcenterscnt.ptr.p_int[i]>0, "BuildRBFModelLSQR: internal error", _state);
|
|
maxnearcenterscnt = ae_maxint(maxnearcenterscnt, nearcenterscnt.ptr.p_int[i], _state);
|
|
maxnearpointscnt = ae_maxint(maxnearpointscnt, nearpointscnt.ptr.p_int[i], _state);
|
|
maxfarpointscnt = ae_maxint(maxfarpointscnt, farpointscnt.ptr.p_int[i], _state);
|
|
sumnearcenterscnt = sumnearcenterscnt+nearcenterscnt.ptr.p_int[i];
|
|
sumnearpointscnt = sumnearpointscnt+nearpointscnt.ptr.p_int[i];
|
|
sumfarpointscnt = sumfarpointscnt+farpointscnt.ptr.p_int[i];
|
|
}
|
|
*snnz = sumnearcenterscnt;
|
|
*gnnz = sumfarpointscnt;
|
|
ae_assert(maxnearcenterscnt>0, "BuildRBFModelLSQR: internal error", _state);
|
|
|
|
/*
|
|
* Allocate temporaries.
|
|
*
|
|
* NOTE: we want to avoid allocation of zero-size arrays, so we
|
|
* use max(desired_size,1) instead of desired_size when performing
|
|
* memory allocation.
|
|
*/
|
|
ae_matrix_set_length(&a, maxnearpointscnt+maxnearcenterscnt, maxnearcenterscnt, _state);
|
|
ae_vector_set_length(&tmpy, maxnearpointscnt+maxnearcenterscnt, _state);
|
|
ae_vector_set_length(&g, maxnearcenterscnt, _state);
|
|
ae_vector_set_length(&c, maxnearcenterscnt, _state);
|
|
ae_matrix_set_length(&nearcenters, maxnearcenterscnt, rbf_mxnx, _state);
|
|
ae_matrix_set_length(&nearpoints, ae_maxint(maxnearpointscnt, 1, _state), rbf_mxnx, _state);
|
|
ae_matrix_set_length(&farpoints, ae_maxint(maxfarpointscnt, 1, _state), rbf_mxnx, _state);
|
|
|
|
/*
|
|
* fill matrix SpG
|
|
*/
|
|
sparsecreate(n, nc, *gnnz, &spg, _state);
|
|
sparsecreate(nc, nc, *snnz, &sps, _state);
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
centerscnt = nearcenterscnt.ptr.p_int[i];
|
|
|
|
/*
|
|
* main center
|
|
*/
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xcx.ptr.p_double[j] = xc->ptr.pp_double[i][j];
|
|
}
|
|
|
|
/*
|
|
* center's tree
|
|
*/
|
|
tmpi = kdtreequeryknn(centerstree, &xcx, centerscnt, ae_true, _state);
|
|
ae_assert(tmpi==centerscnt, "BuildRBFModelLSQR: internal error", _state);
|
|
kdtreequeryresultsx(centerstree, &cx, _state);
|
|
kdtreequeryresultstags(centerstree, ¢erstags, _state);
|
|
|
|
/*
|
|
* point's tree
|
|
*/
|
|
mrad = 0;
|
|
for(j=0; j<=centerscnt-1; j++)
|
|
{
|
|
mrad = ae_maxreal(mrad, r->ptr.p_double[centerstags.ptr.p_int[j]], _state);
|
|
}
|
|
|
|
/*
|
|
* we need to be sure that 'CTree' contains
|
|
* at least one side center
|
|
*/
|
|
sparseset(&sps, i, i, 1, _state);
|
|
c.ptr.p_double[0] = 1.0;
|
|
for(j=1; j<=centerscnt-1; j++)
|
|
{
|
|
c.ptr.p_double[j] = 0.0;
|
|
}
|
|
if( centerscnt>1&&nearpointscnt.ptr.p_int[i]>0 )
|
|
{
|
|
|
|
/*
|
|
* first KDTree request for points
|
|
*/
|
|
pointscnt = nearpointscnt.ptr.p_int[i];
|
|
tmpi = kdtreequeryknn(pointstree, &xcx, skipnearpointscnt.ptr.p_int[i]+nearpointscnt.ptr.p_int[i], ae_true, _state);
|
|
ae_assert(tmpi==skipnearpointscnt.ptr.p_int[i]+nearpointscnt.ptr.p_int[i], "BuildRBFModelLSQR: internal error", _state);
|
|
kdtreequeryresultsx(pointstree, &xx, _state);
|
|
sind = skipnearpointscnt.ptr.p_int[i];
|
|
for(j=0; j<=pointscnt-1; j++)
|
|
{
|
|
vx = xx.ptr.pp_double[sind+j][0];
|
|
vy = xx.ptr.pp_double[sind+j][1];
|
|
vz = xx.ptr.pp_double[sind+j][2];
|
|
for(k=0; k<=centerscnt-1; k++)
|
|
{
|
|
vr = 0.0;
|
|
vv = vx-cx.ptr.pp_double[k][0];
|
|
vr = vr+vv*vv;
|
|
vv = vy-cx.ptr.pp_double[k][1];
|
|
vr = vr+vv*vv;
|
|
vv = vz-cx.ptr.pp_double[k][2];
|
|
vr = vr+vv*vv;
|
|
vv = r->ptr.p_double[centerstags.ptr.p_int[k]];
|
|
a.ptr.pp_double[j][k] = ae_exp(-vr/(vv*vv), _state);
|
|
}
|
|
}
|
|
for(j=0; j<=centerscnt-1; j++)
|
|
{
|
|
g.ptr.p_double[j] = ae_exp(-(ae_sqr(xcx.ptr.p_double[0]-cx.ptr.pp_double[j][0], _state)+ae_sqr(xcx.ptr.p_double[1]-cx.ptr.pp_double[j][1], _state)+ae_sqr(xcx.ptr.p_double[2]-cx.ptr.pp_double[j][2], _state))/ae_sqr(r->ptr.p_double[centerstags.ptr.p_int[j]], _state), _state);
|
|
}
|
|
|
|
/*
|
|
* calculate the problem
|
|
*/
|
|
gnorm2 = ae_v_dotproduct(&g.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,centerscnt-1));
|
|
for(j=0; j<=pointscnt-1; j++)
|
|
{
|
|
vv = ae_v_dotproduct(&a.ptr.pp_double[j][0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,centerscnt-1));
|
|
vv = vv/gnorm2;
|
|
tmpy.ptr.p_double[j] = -vv;
|
|
ae_v_subd(&a.ptr.pp_double[j][0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,centerscnt-1), vv);
|
|
}
|
|
for(j=pointscnt; j<=pointscnt+centerscnt-1; j++)
|
|
{
|
|
for(k=0; k<=centerscnt-1; k++)
|
|
{
|
|
a.ptr.pp_double[j][k] = 0.0;
|
|
}
|
|
a.ptr.pp_double[j][j-pointscnt] = 1.0E-6;
|
|
tmpy.ptr.p_double[j] = 0.0;
|
|
}
|
|
fblssolvels(&a, &tmpy, pointscnt+centerscnt, centerscnt, &tmp0, &tmp1, &tmp2, _state);
|
|
ae_v_move(&c.ptr.p_double[0], 1, &tmpy.ptr.p_double[0], 1, ae_v_len(0,centerscnt-1));
|
|
vv = ae_v_dotproduct(&g.ptr.p_double[0], 1, &c.ptr.p_double[0], 1, ae_v_len(0,centerscnt-1));
|
|
vv = vv/gnorm2;
|
|
ae_v_subd(&c.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,centerscnt-1), vv);
|
|
vv = 1/gnorm2;
|
|
ae_v_addd(&c.ptr.p_double[0], 1, &g.ptr.p_double[0], 1, ae_v_len(0,centerscnt-1), vv);
|
|
for(j=0; j<=centerscnt-1; j++)
|
|
{
|
|
sparseset(&sps, i, centerstags.ptr.p_int[j], c.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* second KDTree request for points
|
|
*/
|
|
pointscnt = farpointscnt.ptr.p_int[i];
|
|
tmpi = kdtreequeryknn(pointstree, &xcx, pointscnt, ae_true, _state);
|
|
ae_assert(tmpi==pointscnt, "BuildRBFModelLSQR: internal error", _state);
|
|
kdtreequeryresultsx(pointstree, &xx, _state);
|
|
kdtreequeryresultstags(pointstree, &pointstags, _state);
|
|
|
|
/*
|
|
*fill SpG matrix
|
|
*/
|
|
for(j=0; j<=pointscnt-1; j++)
|
|
{
|
|
fx = 0;
|
|
vx = xx.ptr.pp_double[j][0];
|
|
vy = xx.ptr.pp_double[j][1];
|
|
vz = xx.ptr.pp_double[j][2];
|
|
for(k=0; k<=centerscnt-1; k++)
|
|
{
|
|
vr = 0.0;
|
|
vv = vx-cx.ptr.pp_double[k][0];
|
|
vr = vr+vv*vv;
|
|
vv = vy-cx.ptr.pp_double[k][1];
|
|
vr = vr+vv*vv;
|
|
vv = vz-cx.ptr.pp_double[k][2];
|
|
vr = vr+vv*vv;
|
|
vv = r->ptr.p_double[centerstags.ptr.p_int[k]];
|
|
vv = vv*vv;
|
|
fx = fx+c.ptr.p_double[k]*ae_exp(-vr/vv, _state);
|
|
}
|
|
sparseset(&spg, pointstags.ptr.p_int[j], i, fx, _state);
|
|
}
|
|
}
|
|
sparseconverttocrs(&spg, _state);
|
|
sparseconverttocrs(&sps, _state);
|
|
|
|
/*
|
|
* solve by LSQR method
|
|
*/
|
|
ae_vector_set_length(&tmpy, n, _state);
|
|
ae_vector_set_length(&tc, nc, _state);
|
|
ae_matrix_set_length(w, nc, ny, _state);
|
|
linlsqrcreate(n, nc, &state, _state);
|
|
linlsqrsetcond(&state, epsort, epserr, maxits, _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
tmpy.ptr.p_double[j] = y->ptr.pp_double[j][i];
|
|
}
|
|
linlsqrsolvesparse(&state, &spg, &tmpy, _state);
|
|
linlsqrresults(&state, &c, &lsqrrep, _state);
|
|
if( lsqrrep.terminationtype<=0 )
|
|
{
|
|
*info = -4;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
sparsemtv(&sps, &c, &tc, _state);
|
|
for(j=0; j<=nc-1; j++)
|
|
{
|
|
w->ptr.pp_double[j][i] = tc.ptr.p_double[j];
|
|
}
|
|
*iterationscount = *iterationscount+lsqrrep.iterationscount;
|
|
*nmv = *nmv+lsqrrep.nmv;
|
|
}
|
|
*info = 1;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
static void rbf_buildrbfmlayersmodellsqr(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
/* Real */ ae_matrix* xc,
|
|
double rval,
|
|
/* Real */ ae_vector* r,
|
|
ae_int_t n,
|
|
ae_int_t* nc,
|
|
ae_int_t ny,
|
|
ae_int_t nlayers,
|
|
kdtree* centerstree,
|
|
double epsort,
|
|
double epserr,
|
|
ae_int_t maxits,
|
|
double lambdav,
|
|
ae_int_t* annz,
|
|
/* Real */ ae_matrix* w,
|
|
ae_int_t* info,
|
|
ae_int_t* iterationscount,
|
|
ae_int_t* nmv,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
linlsqrstate state;
|
|
linlsqrreport lsqrrep;
|
|
sparsematrix spa;
|
|
double anorm;
|
|
ae_vector omega;
|
|
ae_vector xx;
|
|
ae_vector tmpy;
|
|
ae_matrix cx;
|
|
double yval;
|
|
ae_int_t nec;
|
|
ae_vector centerstags;
|
|
ae_int_t layer;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
double v;
|
|
double rmaxbefore;
|
|
double rmaxafter;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_clear(xc);
|
|
ae_vector_clear(r);
|
|
*nc = 0;
|
|
*annz = 0;
|
|
ae_matrix_clear(w);
|
|
*info = 0;
|
|
*iterationscount = 0;
|
|
*nmv = 0;
|
|
_linlsqrstate_init(&state, _state, ae_true);
|
|
_linlsqrreport_init(&lsqrrep, _state, ae_true);
|
|
_sparsematrix_init(&spa, _state, ae_true);
|
|
ae_vector_init(&omega, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpy, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&cx, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(¢erstags, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_assert(nlayers>=0, "BuildRBFMLayersModelLSQR: invalid argument(NLayers<0)", _state);
|
|
ae_assert(n>=0, "BuildRBFMLayersModelLSQR: invalid argument(N<0)", _state);
|
|
ae_assert(rbf_mxnx>0&&rbf_mxnx<=3, "BuildRBFMLayersModelLSQR: internal error(invalid global const MxNX: either MxNX<=0 or MxNX>3)", _state);
|
|
*annz = 0;
|
|
if( n==0||nlayers==0 )
|
|
{
|
|
*info = 1;
|
|
*iterationscount = 0;
|
|
*nmv = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
*nc = n*nlayers;
|
|
ae_vector_set_length(&xx, rbf_mxnx, _state);
|
|
ae_vector_set_length(¢erstags, n, _state);
|
|
ae_matrix_set_length(xc, *nc, rbf_mxnx, _state);
|
|
ae_vector_set_length(r, *nc, _state);
|
|
for(i=0; i<=*nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xc->ptr.pp_double[i][j] = x->ptr.pp_double[i%n][j];
|
|
}
|
|
}
|
|
for(i=0; i<=*nc-1; i++)
|
|
{
|
|
r->ptr.p_double[i] = rval/ae_pow(2, i/n, _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
centerstags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(xc, ¢erstags, n, rbf_mxnx, 0, 2, centerstree, _state);
|
|
ae_vector_set_length(&omega, n, _state);
|
|
ae_vector_set_length(&tmpy, n, _state);
|
|
ae_matrix_set_length(w, *nc, ny, _state);
|
|
*info = -1;
|
|
*iterationscount = 0;
|
|
*nmv = 0;
|
|
linlsqrcreate(n, n, &state, _state);
|
|
linlsqrsetcond(&state, epsort, epserr, maxits, _state);
|
|
linlsqrsetlambdai(&state, 1.0E-6, _state);
|
|
|
|
/*
|
|
* calculate number of non-zero elements for sparse matrix
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xx.ptr.p_double[j] = x->ptr.pp_double[i][j];
|
|
}
|
|
*annz = *annz+kdtreequeryrnn(centerstree, &xx, r->ptr.p_double[0]*rbf_rbfmlradius, ae_true, _state);
|
|
}
|
|
for(layer=0; layer<=nlayers-1; layer++)
|
|
{
|
|
|
|
/*
|
|
* Fill sparse matrix, calculate norm(A)
|
|
*/
|
|
anorm = 0.0;
|
|
sparsecreate(n, n, *annz, &spa, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=rbf_mxnx-1; j++)
|
|
{
|
|
xx.ptr.p_double[j] = x->ptr.pp_double[i][j];
|
|
}
|
|
nec = kdtreequeryrnn(centerstree, &xx, r->ptr.p_double[layer*n]*rbf_rbfmlradius, ae_true, _state);
|
|
kdtreequeryresultsx(centerstree, &cx, _state);
|
|
kdtreequeryresultstags(centerstree, ¢erstags, _state);
|
|
for(j=0; j<=nec-1; j++)
|
|
{
|
|
v = ae_exp(-(ae_sqr(xx.ptr.p_double[0]-cx.ptr.pp_double[j][0], _state)+ae_sqr(xx.ptr.p_double[1]-cx.ptr.pp_double[j][1], _state)+ae_sqr(xx.ptr.p_double[2]-cx.ptr.pp_double[j][2], _state))/ae_sqr(r->ptr.p_double[layer*n+centerstags.ptr.p_int[j]], _state), _state);
|
|
sparseset(&spa, i, centerstags.ptr.p_int[j], v, _state);
|
|
anorm = anorm+ae_sqr(v, _state);
|
|
}
|
|
}
|
|
anorm = ae_sqrt(anorm, _state);
|
|
sparseconverttocrs(&spa, _state);
|
|
|
|
/*
|
|
* Calculate maximum residual before adding new layer.
|
|
* This value is not used by algorithm, the only purpose is to make debugging easier.
|
|
*/
|
|
rmaxbefore = 0.0;
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
rmaxbefore = ae_maxreal(rmaxbefore, ae_fabs(y->ptr.pp_double[j][i], _state), _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process NY dimensions of the target function
|
|
*/
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
tmpy.ptr.p_double[j] = y->ptr.pp_double[j][i];
|
|
}
|
|
|
|
/*
|
|
* calculate Omega for current layer
|
|
*/
|
|
linlsqrsetlambdai(&state, lambdav*anorm/n, _state);
|
|
linlsqrsolvesparse(&state, &spa, &tmpy, _state);
|
|
linlsqrresults(&state, &omega, &lsqrrep, _state);
|
|
if( lsqrrep.terminationtype<=0 )
|
|
{
|
|
*info = -4;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* calculate error for current layer
|
|
*/
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
yval = 0;
|
|
for(k=0; k<=rbf_mxnx-1; k++)
|
|
{
|
|
xx.ptr.p_double[k] = x->ptr.pp_double[j][k];
|
|
}
|
|
nec = kdtreequeryrnn(centerstree, &xx, r->ptr.p_double[layer*n]*rbf_rbffarradius, ae_true, _state);
|
|
kdtreequeryresultsx(centerstree, &cx, _state);
|
|
kdtreequeryresultstags(centerstree, ¢erstags, _state);
|
|
for(k=0; k<=nec-1; k++)
|
|
{
|
|
yval = yval+omega.ptr.p_double[centerstags.ptr.p_int[k]]*ae_exp(-(ae_sqr(xx.ptr.p_double[0]-cx.ptr.pp_double[k][0], _state)+ae_sqr(xx.ptr.p_double[1]-cx.ptr.pp_double[k][1], _state)+ae_sqr(xx.ptr.p_double[2]-cx.ptr.pp_double[k][2], _state))/ae_sqr(r->ptr.p_double[layer*n+centerstags.ptr.p_int[k]], _state), _state);
|
|
}
|
|
y->ptr.pp_double[j][i] = y->ptr.pp_double[j][i]-yval;
|
|
}
|
|
|
|
/*
|
|
* write Omega in out parameter W
|
|
*/
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
w->ptr.pp_double[layer*n+j][i] = omega.ptr.p_double[j];
|
|
}
|
|
*iterationscount = *iterationscount+lsqrrep.iterationscount;
|
|
*nmv = *nmv+lsqrrep.nmv;
|
|
}
|
|
|
|
/*
|
|
* Calculate maximum residual before adding new layer.
|
|
* This value is not used by algorithm, the only purpose is to make debugging easier.
|
|
*/
|
|
rmaxafter = 0.0;
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
rmaxafter = ae_maxreal(rmaxafter, ae_fabs(y->ptr.pp_double[j][i], _state), _state);
|
|
}
|
|
}
|
|
}
|
|
*info = 1;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
ae_bool _rbfmodel_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfmodel *p = (rbfmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !_kdtree_init(&p->tree, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->xc, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->wr, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->v, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->x, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->y, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->calcbufxcx, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init(&p->calcbufx, 0, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->calcbuftags, 0, DT_INT, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _rbfmodel_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfmodel *dst = (rbfmodel*)_dst;
|
|
rbfmodel *src = (rbfmodel*)_src;
|
|
dst->ny = src->ny;
|
|
dst->nx = src->nx;
|
|
dst->nc = src->nc;
|
|
dst->nl = src->nl;
|
|
if( !_kdtree_init_copy(&dst->tree, &src->tree, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->xc, &src->xc, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->wr, &src->wr, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->rmax = src->rmax;
|
|
if( !ae_matrix_init_copy(&dst->v, &src->v, _state, make_automatic) )
|
|
return ae_false;
|
|
dst->gridtype = src->gridtype;
|
|
dst->fixrad = src->fixrad;
|
|
dst->lambdav = src->lambdav;
|
|
dst->radvalue = src->radvalue;
|
|
dst->radzvalue = src->radzvalue;
|
|
dst->nlayers = src->nlayers;
|
|
dst->aterm = src->aterm;
|
|
dst->algorithmtype = src->algorithmtype;
|
|
dst->epsort = src->epsort;
|
|
dst->epserr = src->epserr;
|
|
dst->maxits = src->maxits;
|
|
dst->h = src->h;
|
|
dst->n = src->n;
|
|
if( !ae_matrix_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->y, &src->y, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->calcbufxcx, &src->calcbufxcx, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_matrix_init_copy(&dst->calcbufx, &src->calcbufx, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->calcbuftags, &src->calcbuftags, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _rbfmodel_clear(void* _p)
|
|
{
|
|
rbfmodel *p = (rbfmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_kdtree_clear(&p->tree);
|
|
ae_matrix_clear(&p->xc);
|
|
ae_matrix_clear(&p->wr);
|
|
ae_matrix_clear(&p->v);
|
|
ae_matrix_clear(&p->x);
|
|
ae_matrix_clear(&p->y);
|
|
ae_vector_clear(&p->calcbufxcx);
|
|
ae_matrix_clear(&p->calcbufx);
|
|
ae_vector_clear(&p->calcbuftags);
|
|
}
|
|
|
|
|
|
void _rbfmodel_destroy(void* _p)
|
|
{
|
|
rbfmodel *p = (rbfmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_kdtree_destroy(&p->tree);
|
|
ae_matrix_destroy(&p->xc);
|
|
ae_matrix_destroy(&p->wr);
|
|
ae_matrix_destroy(&p->v);
|
|
ae_matrix_destroy(&p->x);
|
|
ae_matrix_destroy(&p->y);
|
|
ae_vector_destroy(&p->calcbufxcx);
|
|
ae_matrix_destroy(&p->calcbufx);
|
|
ae_vector_destroy(&p->calcbuftags);
|
|
}
|
|
|
|
|
|
ae_bool _rbfreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfreport *p = (rbfreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _rbfreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfreport *dst = (rbfreport*)_dst;
|
|
rbfreport *src = (rbfreport*)_src;
|
|
dst->arows = src->arows;
|
|
dst->acols = src->acols;
|
|
dst->annz = src->annz;
|
|
dst->iterationscount = src->iterationscount;
|
|
dst->nmv = src->nmv;
|
|
dst->terminationtype = src->terminationtype;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _rbfreport_clear(void* _p)
|
|
{
|
|
rbfreport *p = (rbfreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _rbfreport_destroy(void* _p)
|
|
{
|
|
rbfreport *p = (rbfreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the bilinear or bicubic spline at
|
|
the given point X.
|
|
|
|
Input parameters:
|
|
C - coefficients table.
|
|
Built by BuildBilinearSpline or BuildBicubicSpline.
|
|
X, Y- point
|
|
|
|
Result:
|
|
S(x,y)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline2dcalc(spline2dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
double vx;
|
|
double vy;
|
|
double vxy;
|
|
double result;
|
|
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline2DCalc: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(x, _state)&&ae_isfinite(y, _state), "Spline2DCalc: X or Y contains NaN or Infinite value", _state);
|
|
if( c->d!=1 )
|
|
{
|
|
result = 0;
|
|
return result;
|
|
}
|
|
spline2ddiff(c, x, y, &v, &vx, &vy, &vxy, _state);
|
|
result = v;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the bilinear or bicubic spline at
|
|
the given point X and its derivatives.
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
|
|
Output parameters:
|
|
F - S(x,y)
|
|
FX - dS(x,y)/dX
|
|
FY - dS(x,y)/dY
|
|
FXY - d2S(x,y)/dXdY
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2ddiff(spline2dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
double* f,
|
|
double* fx,
|
|
double* fy,
|
|
double* fxy,
|
|
ae_state *_state)
|
|
{
|
|
double t;
|
|
double dt;
|
|
double u;
|
|
double du;
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t h;
|
|
ae_int_t s1;
|
|
ae_int_t s2;
|
|
ae_int_t s3;
|
|
ae_int_t s4;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
double y1;
|
|
double y2;
|
|
double y3;
|
|
double y4;
|
|
double v;
|
|
double t0;
|
|
double t1;
|
|
double t2;
|
|
double t3;
|
|
double u0;
|
|
double u1;
|
|
double u2;
|
|
double u3;
|
|
|
|
*f = 0;
|
|
*fx = 0;
|
|
*fy = 0;
|
|
*fxy = 0;
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline2DDiff: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(x, _state)&&ae_isfinite(y, _state), "Spline2DDiff: X or Y contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Prepare F, dF/dX, dF/dY, d2F/dXdY
|
|
*/
|
|
*f = 0;
|
|
*fx = 0;
|
|
*fy = 0;
|
|
*fxy = 0;
|
|
if( c->d!=1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->n-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->x.ptr.p_double[h],x) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
t = (x-c->x.ptr.p_double[l])/(c->x.ptr.p_double[l+1]-c->x.ptr.p_double[l]);
|
|
dt = 1.0/(c->x.ptr.p_double[l+1]-c->x.ptr.p_double[l]);
|
|
ix = l;
|
|
|
|
/*
|
|
* Binary search in the [ y[0], ..., y[m-2] ] (y[m-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->m-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->y.ptr.p_double[h],y) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
u = (y-c->y.ptr.p_double[l])/(c->y.ptr.p_double[l+1]-c->y.ptr.p_double[l]);
|
|
du = 1.0/(c->y.ptr.p_double[l+1]-c->y.ptr.p_double[l]);
|
|
iy = l;
|
|
|
|
/*
|
|
* Bilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
y1 = c->f.ptr.p_double[c->n*iy+ix];
|
|
y2 = c->f.ptr.p_double[c->n*iy+(ix+1)];
|
|
y3 = c->f.ptr.p_double[c->n*(iy+1)+(ix+1)];
|
|
y4 = c->f.ptr.p_double[c->n*(iy+1)+ix];
|
|
*f = (1-t)*(1-u)*y1+t*(1-u)*y2+t*u*y3+(1-t)*u*y4;
|
|
*fx = (-(1-u)*y1+(1-u)*y2+u*y3-u*y4)*dt;
|
|
*fy = (-(1-t)*y1-t*y2+t*y3+(1-t)*y4)*du;
|
|
*fxy = (y1-y2+y3-y4)*du*dt;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Bicubic interpolation
|
|
*/
|
|
if( c->stype==-3 )
|
|
{
|
|
|
|
/*
|
|
* Prepare info
|
|
*/
|
|
t0 = 1;
|
|
t1 = t;
|
|
t2 = ae_sqr(t, _state);
|
|
t3 = t*t2;
|
|
u0 = 1;
|
|
u1 = u;
|
|
u2 = ae_sqr(u, _state);
|
|
u3 = u*u2;
|
|
sfx = c->n*c->m;
|
|
sfy = 2*c->n*c->m;
|
|
sfxy = 3*c->n*c->m;
|
|
s1 = c->n*iy+ix;
|
|
s2 = c->n*iy+(ix+1);
|
|
s3 = c->n*(iy+1)+(ix+1);
|
|
s4 = c->n*(iy+1)+ix;
|
|
|
|
/*
|
|
* Calculate
|
|
*/
|
|
v = c->f.ptr.p_double[s1];
|
|
*f = *f+v*t0*u0;
|
|
v = c->f.ptr.p_double[sfy+s1]/du;
|
|
*f = *f+v*t0*u1;
|
|
*fy = *fy+v*t0*u0*du;
|
|
v = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s4]-2*c->f.ptr.p_double[sfy+s1]/du-c->f.ptr.p_double[sfy+s4]/du;
|
|
*f = *f+v*t0*u2;
|
|
*fy = *fy+2*v*t0*u1*du;
|
|
v = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s4]+c->f.ptr.p_double[sfy+s1]/du+c->f.ptr.p_double[sfy+s4]/du;
|
|
*f = *f+v*t0*u3;
|
|
*fy = *fy+3*v*t0*u2*du;
|
|
v = c->f.ptr.p_double[sfx+s1]/dt;
|
|
*f = *f+v*t1*u0;
|
|
*fx = *fx+v*t0*u0*dt;
|
|
v = c->f.ptr.p_double[sfxy+s1]/(dt*du);
|
|
*f = *f+v*t1*u1;
|
|
*fx = *fx+v*t0*u1*dt;
|
|
*fy = *fy+v*t1*u0*du;
|
|
*fxy = *fxy+v*t0*u0*dt*du;
|
|
v = -3*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
*f = *f+v*t1*u2;
|
|
*fx = *fx+v*t0*u2*dt;
|
|
*fy = *fy+2*v*t1*u1*du;
|
|
*fxy = *fxy+2*v*t0*u1*dt*du;
|
|
v = 2*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
*f = *f+v*t1*u3;
|
|
*fx = *fx+v*t0*u3*dt;
|
|
*fy = *fy+3*v*t1*u2*du;
|
|
*fxy = *fxy+3*v*t0*u2*dt*du;
|
|
v = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s2]-2*c->f.ptr.p_double[sfx+s1]/dt-c->f.ptr.p_double[sfx+s2]/dt;
|
|
*f = *f+v*t2*u0;
|
|
*fx = *fx+2*v*t1*u0*dt;
|
|
v = -3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
*f = *f+v*t2*u1;
|
|
*fx = *fx+2*v*t1*u1*dt;
|
|
*fy = *fy+v*t2*u0*du;
|
|
*fxy = *fxy+2*v*t1*u0*dt*du;
|
|
v = 9*c->f.ptr.p_double[s1]-9*c->f.ptr.p_double[s2]+9*c->f.ptr.p_double[s3]-9*c->f.ptr.p_double[s4]+6*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s2]/dt-3*c->f.ptr.p_double[sfx+s3]/dt-6*c->f.ptr.p_double[sfx+s4]/dt+6*c->f.ptr.p_double[sfy+s1]/du-6*c->f.ptr.p_double[sfy+s2]/du-3*c->f.ptr.p_double[sfy+s3]/du+3*c->f.ptr.p_double[sfy+s4]/du+4*c->f.ptr.p_double[sfxy+s1]/(dt*du)+2*c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
*f = *f+v*t2*u2;
|
|
*fx = *fx+2*v*t1*u2*dt;
|
|
*fy = *fy+2*v*t2*u1*du;
|
|
*fxy = *fxy+4*v*t1*u1*dt*du;
|
|
v = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-4*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s2]/dt+2*c->f.ptr.p_double[sfx+s3]/dt+4*c->f.ptr.p_double[sfx+s4]/dt-3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du+3*c->f.ptr.p_double[sfy+s3]/du-3*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
*f = *f+v*t2*u3;
|
|
*fx = *fx+2*v*t1*u3*dt;
|
|
*fy = *fy+3*v*t2*u2*du;
|
|
*fxy = *fxy+6*v*t1*u2*dt*du;
|
|
v = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s2]+c->f.ptr.p_double[sfx+s1]/dt+c->f.ptr.p_double[sfx+s2]/dt;
|
|
*f = *f+v*t3*u0;
|
|
*fx = *fx+3*v*t2*u0*dt;
|
|
v = 2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
*f = *f+v*t3*u1;
|
|
*fx = *fx+3*v*t2*u1*dt;
|
|
*fy = *fy+v*t3*u0*du;
|
|
*fxy = *fxy+3*v*t2*u0*dt*du;
|
|
v = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-3*c->f.ptr.p_double[sfx+s1]/dt-3*c->f.ptr.p_double[sfx+s2]/dt+3*c->f.ptr.p_double[sfx+s3]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-4*c->f.ptr.p_double[sfy+s1]/du+4*c->f.ptr.p_double[sfy+s2]/du+2*c->f.ptr.p_double[sfy+s3]/du-2*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-2*c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
*f = *f+v*t3*u2;
|
|
*fx = *fx+3*v*t2*u2*dt;
|
|
*fy = *fy+2*v*t3*u1*du;
|
|
*fxy = *fxy+6*v*t2*u1*dt*du;
|
|
v = 4*c->f.ptr.p_double[s1]-4*c->f.ptr.p_double[s2]+4*c->f.ptr.p_double[s3]-4*c->f.ptr.p_double[s4]+2*c->f.ptr.p_double[sfx+s1]/dt+2*c->f.ptr.p_double[sfx+s2]/dt-2*c->f.ptr.p_double[sfx+s3]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfy+s3]/du+2*c->f.ptr.p_double[sfy+s4]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
*f = *f+v*t3*u3;
|
|
*fx = *fx+3*v*t2*u3*dt;
|
|
*fy = *fy+3*v*t3*u2*du;
|
|
*fxy = *fxy+9*v*t2*u2*dt*du;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline argument.
|
|
|
|
Input parameters:
|
|
C - spline interpolant
|
|
AX, BX - transformation coefficients: x = A*t + B
|
|
AY, BY - transformation coefficients: y = A*u + B
|
|
Result:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dlintransxy(spline2dinterpolant* c,
|
|
double ax,
|
|
double bx,
|
|
double ay,
|
|
double by,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector x;
|
|
ae_vector y;
|
|
ae_vector f;
|
|
ae_vector v;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&f, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&v, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(c->stype==-3||c->stype==-1, "Spline2DLinTransXY: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(ax, _state), "Spline2DLinTransXY: AX is infinite or NaN", _state);
|
|
ae_assert(ae_isfinite(bx, _state), "Spline2DLinTransXY: BX is infinite or NaN", _state);
|
|
ae_assert(ae_isfinite(ay, _state), "Spline2DLinTransXY: AY is infinite or NaN", _state);
|
|
ae_assert(ae_isfinite(by, _state), "Spline2DLinTransXY: BY is infinite or NaN", _state);
|
|
ae_vector_set_length(&x, c->n, _state);
|
|
ae_vector_set_length(&y, c->m, _state);
|
|
ae_vector_set_length(&f, c->m*c->n*c->d, _state);
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
x.ptr.p_double[j] = c->x.ptr.p_double[j];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
y.ptr.p_double[i] = c->y.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(k=0; k<=c->d-1; k++)
|
|
{
|
|
f.ptr.p_double[c->d*(i*c->n+j)+k] = c->f.ptr.p_double[c->d*(i*c->n+j)+k];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle different combinations of AX/AY
|
|
*/
|
|
if( ae_fp_eq(ax,0)&&ae_fp_neq(ay,0) )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
spline2dcalcvbuf(c, bx, y.ptr.p_double[i], &v, _state);
|
|
y.ptr.p_double[i] = (y.ptr.p_double[i]-by)/ay;
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(k=0; k<=c->d-1; k++)
|
|
{
|
|
f.ptr.p_double[c->d*(i*c->n+j)+k] = v.ptr.p_double[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( ae_fp_neq(ax,0)&&ae_fp_eq(ay,0) )
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
spline2dcalcvbuf(c, x.ptr.p_double[j], by, &v, _state);
|
|
x.ptr.p_double[j] = (x.ptr.p_double[j]-bx)/ax;
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(k=0; k<=c->d-1; k++)
|
|
{
|
|
f.ptr.p_double[c->d*(i*c->n+j)+k] = v.ptr.p_double[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( ae_fp_neq(ax,0)&&ae_fp_neq(ay,0) )
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
x.ptr.p_double[j] = (x.ptr.p_double[j]-bx)/ax;
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
y.ptr.p_double[i] = (y.ptr.p_double[i]-by)/ay;
|
|
}
|
|
}
|
|
if( ae_fp_eq(ax,0)&&ae_fp_eq(ay,0) )
|
|
{
|
|
spline2dcalcvbuf(c, bx, by, &v, _state);
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(k=0; k<=c->d-1; k++)
|
|
{
|
|
f.ptr.p_double[c->d*(i*c->n+j)+k] = v.ptr.p_double[k];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Rebuild spline
|
|
*/
|
|
if( c->stype==-3 )
|
|
{
|
|
spline2dbuildbicubicv(&x, c->n, &y, c->m, &f, c->d, c, _state);
|
|
}
|
|
if( c->stype==-1 )
|
|
{
|
|
spline2dbuildbilinearv(&x, c->n, &y, c->m, &f, c->d, c, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline.
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: S2(x,y) = A*S(x,y) + B
|
|
|
|
Output parameters:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 30.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dlintransf(spline2dinterpolant* c,
|
|
double a,
|
|
double b,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector x;
|
|
ae_vector y;
|
|
ae_vector f;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&f, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(c->stype==-3||c->stype==-1, "Spline2DLinTransF: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_vector_set_length(&x, c->n, _state);
|
|
ae_vector_set_length(&y, c->m, _state);
|
|
ae_vector_set_length(&f, c->m*c->n*c->d, _state);
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
x.ptr.p_double[j] = c->x.ptr.p_double[j];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
y.ptr.p_double[i] = c->y.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m*c->n*c->d-1; i++)
|
|
{
|
|
f.ptr.p_double[i] = a*c->f.ptr.p_double[i]+b;
|
|
}
|
|
if( c->stype==-3 )
|
|
{
|
|
spline2dbuildbicubicv(&x, c->n, &y, c->m, &f, c->d, c, _state);
|
|
}
|
|
if( c->stype==-1 )
|
|
{
|
|
spline2dbuildbilinearv(&x, c->n, &y, c->m, &f, c->d, c, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine makes the copy of the spline model.
|
|
|
|
Input parameters:
|
|
C - spline interpolant
|
|
|
|
Output parameters:
|
|
CC - spline copy
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcopy(spline2dinterpolant* c,
|
|
spline2dinterpolant* cc,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t tblsize;
|
|
|
|
_spline2dinterpolant_clear(cc);
|
|
|
|
ae_assert(c->k==1||c->k==3, "Spline2DCopy: incorrect C (incorrect parameter C.K)", _state);
|
|
cc->k = c->k;
|
|
cc->n = c->n;
|
|
cc->m = c->m;
|
|
cc->d = c->d;
|
|
cc->stype = c->stype;
|
|
tblsize = -1;
|
|
if( c->stype==-3 )
|
|
{
|
|
tblsize = 4*c->n*c->m*c->d;
|
|
}
|
|
if( c->stype==-1 )
|
|
{
|
|
tblsize = c->n*c->m*c->d;
|
|
}
|
|
ae_assert(tblsize>0, "Spline2DCopy: internal error", _state);
|
|
ae_vector_set_length(&cc->x, cc->n, _state);
|
|
ae_vector_set_length(&cc->y, cc->m, _state);
|
|
ae_vector_set_length(&cc->f, tblsize, _state);
|
|
ae_v_move(&cc->x.ptr.p_double[0], 1, &c->x.ptr.p_double[0], 1, ae_v_len(0,cc->n-1));
|
|
ae_v_move(&cc->y.ptr.p_double[0], 1, &c->y.ptr.p_double[0], 1, ae_v_len(0,cc->m-1));
|
|
ae_v_move(&cc->f.ptr.p_double[0], 1, &c->f.ptr.p_double[0], 1, ae_v_len(0,tblsize-1));
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Bicubic spline resampling
|
|
|
|
Input parameters:
|
|
A - function values at the old grid,
|
|
array[0..OldHeight-1, 0..OldWidth-1]
|
|
OldHeight - old grid height, OldHeight>1
|
|
OldWidth - old grid width, OldWidth>1
|
|
NewHeight - new grid height, NewHeight>1
|
|
NewWidth - new grid width, NewWidth>1
|
|
|
|
Output parameters:
|
|
B - function values at the new grid,
|
|
array[0..NewHeight-1, 0..NewWidth-1]
|
|
|
|
-- ALGLIB routine --
|
|
15 May, 2007
|
|
Copyright by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dresamplebicubic(/* Real */ ae_matrix* a,
|
|
ae_int_t oldheight,
|
|
ae_int_t oldwidth,
|
|
/* Real */ ae_matrix* b,
|
|
ae_int_t newheight,
|
|
ae_int_t newwidth,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_matrix buf;
|
|
ae_vector x;
|
|
ae_vector y;
|
|
spline1dinterpolant c;
|
|
ae_int_t mw;
|
|
ae_int_t mh;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_clear(b);
|
|
ae_matrix_init(&buf, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
_spline1dinterpolant_init(&c, _state, ae_true);
|
|
|
|
ae_assert(oldwidth>1&&oldheight>1, "Spline2DResampleBicubic: width/height less than 1", _state);
|
|
ae_assert(newwidth>1&&newheight>1, "Spline2DResampleBicubic: width/height less than 1", _state);
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
mw = ae_maxint(oldwidth, newwidth, _state);
|
|
mh = ae_maxint(oldheight, newheight, _state);
|
|
ae_matrix_set_length(b, newheight, newwidth, _state);
|
|
ae_matrix_set_length(&buf, oldheight, newwidth, _state);
|
|
ae_vector_set_length(&x, ae_maxint(mw, mh, _state), _state);
|
|
ae_vector_set_length(&y, ae_maxint(mw, mh, _state), _state);
|
|
|
|
/*
|
|
* Horizontal interpolation
|
|
*/
|
|
for(i=0; i<=oldheight-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Fill X, Y
|
|
*/
|
|
for(j=0; j<=oldwidth-1; j++)
|
|
{
|
|
x.ptr.p_double[j] = (double)j/(double)(oldwidth-1);
|
|
y.ptr.p_double[j] = a->ptr.pp_double[i][j];
|
|
}
|
|
|
|
/*
|
|
* Interpolate and place result into temporary matrix
|
|
*/
|
|
spline1dbuildcubic(&x, &y, oldwidth, 0, 0.0, 0, 0.0, &c, _state);
|
|
for(j=0; j<=newwidth-1; j++)
|
|
{
|
|
buf.ptr.pp_double[i][j] = spline1dcalc(&c, (double)j/(double)(newwidth-1), _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Vertical interpolation
|
|
*/
|
|
for(j=0; j<=newwidth-1; j++)
|
|
{
|
|
|
|
/*
|
|
* Fill X, Y
|
|
*/
|
|
for(i=0; i<=oldheight-1; i++)
|
|
{
|
|
x.ptr.p_double[i] = (double)i/(double)(oldheight-1);
|
|
y.ptr.p_double[i] = buf.ptr.pp_double[i][j];
|
|
}
|
|
|
|
/*
|
|
* Interpolate and place result into B
|
|
*/
|
|
spline1dbuildcubic(&x, &y, oldheight, 0, 0.0, 0, 0.0, &c, _state);
|
|
for(i=0; i<=newheight-1; i++)
|
|
{
|
|
b->ptr.pp_double[i][j] = spline1dcalc(&c, (double)i/(double)(newheight-1), _state);
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Bilinear spline resampling
|
|
|
|
Input parameters:
|
|
A - function values at the old grid,
|
|
array[0..OldHeight-1, 0..OldWidth-1]
|
|
OldHeight - old grid height, OldHeight>1
|
|
OldWidth - old grid width, OldWidth>1
|
|
NewHeight - new grid height, NewHeight>1
|
|
NewWidth - new grid width, NewWidth>1
|
|
|
|
Output parameters:
|
|
B - function values at the new grid,
|
|
array[0..NewHeight-1, 0..NewWidth-1]
|
|
|
|
-- ALGLIB routine --
|
|
09.07.2007
|
|
Copyright by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dresamplebilinear(/* Real */ ae_matrix* a,
|
|
ae_int_t oldheight,
|
|
ae_int_t oldwidth,
|
|
/* Real */ ae_matrix* b,
|
|
ae_int_t newheight,
|
|
ae_int_t newwidth,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t l;
|
|
ae_int_t c;
|
|
double t;
|
|
double u;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
ae_matrix_clear(b);
|
|
|
|
ae_assert(oldwidth>1&&oldheight>1, "Spline2DResampleBilinear: width/height less than 1", _state);
|
|
ae_assert(newwidth>1&&newheight>1, "Spline2DResampleBilinear: width/height less than 1", _state);
|
|
ae_matrix_set_length(b, newheight, newwidth, _state);
|
|
for(i=0; i<=newheight-1; i++)
|
|
{
|
|
for(j=0; j<=newwidth-1; j++)
|
|
{
|
|
l = i*(oldheight-1)/(newheight-1);
|
|
if( l==oldheight-1 )
|
|
{
|
|
l = oldheight-2;
|
|
}
|
|
u = (double)i/(double)(newheight-1)*(oldheight-1)-l;
|
|
c = j*(oldwidth-1)/(newwidth-1);
|
|
if( c==oldwidth-1 )
|
|
{
|
|
c = oldwidth-2;
|
|
}
|
|
t = (double)(j*(oldwidth-1))/(double)(newwidth-1)-c;
|
|
b->ptr.pp_double[i][j] = (1-t)*(1-u)*a->ptr.pp_double[l][c]+t*(1-u)*a->ptr.pp_double[l][c+1]+t*u*a->ptr.pp_double[l+1][c+1]+(1-t)*u*a->ptr.pp_double[l+1][c];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds bilinear vector-valued spline.
|
|
|
|
Input parameters:
|
|
X - spline abscissas, array[0..N-1]
|
|
Y - spline ordinates, array[0..M-1]
|
|
F - function values, array[0..M*N*D-1]:
|
|
* first D elements store D values at (X[0],Y[0])
|
|
* next D elements store D values at (X[1],Y[0])
|
|
* general form - D function values at (X[i],Y[j]) are stored
|
|
at F[D*(J*N+I)...D*(J*N+I)+D-1].
|
|
M,N - grid size, M>=2, N>=2
|
|
D - vector dimension, D>=1
|
|
|
|
Output parameters:
|
|
C - spline interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbilinearv(/* Real */ ae_vector* x,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t m,
|
|
/* Real */ ae_vector* f,
|
|
ae_int_t d,
|
|
spline2dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
double t;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t i0;
|
|
|
|
_spline2dinterpolant_clear(c);
|
|
|
|
ae_assert(n>=2, "Spline2DBuildBilinearV: N is less then 2", _state);
|
|
ae_assert(m>=2, "Spline2DBuildBilinearV: M is less then 2", _state);
|
|
ae_assert(d>=1, "Spline2DBuildBilinearV: invalid argument D (D<1)", _state);
|
|
ae_assert(x->cnt>=n&&y->cnt>=m, "Spline2DBuildBilinearV: length of X or Y is too short (Length(X/Y)<N/M)", _state);
|
|
ae_assert(isfinitevector(x, n, _state)&&isfinitevector(y, m, _state), "Spline2DBuildBilinearV: X or Y contains NaN or Infinite value", _state);
|
|
k = n*m*d;
|
|
ae_assert(f->cnt>=k, "Spline2DBuildBilinearV: length of F is too short (Length(F)<N*M*D)", _state);
|
|
ae_assert(isfinitevector(f, k, _state), "Spline2DBuildBilinearV: F contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Fill interpolant
|
|
*/
|
|
c->k = 1;
|
|
c->n = n;
|
|
c->m = m;
|
|
c->d = d;
|
|
c->stype = -1;
|
|
ae_vector_set_length(&c->x, c->n, _state);
|
|
ae_vector_set_length(&c->y, c->m, _state);
|
|
ae_vector_set_length(&c->f, k, _state);
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
c->x.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
c->y.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
c->f.ptr.p_double[i] = f->ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Sort points
|
|
*/
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
k = j;
|
|
for(i=j+1; i<=c->n-1; i++)
|
|
{
|
|
if( ae_fp_less(c->x.ptr.p_double[i],c->x.ptr.p_double[k]) )
|
|
{
|
|
k = i;
|
|
}
|
|
}
|
|
if( k!=j )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(i0=0; i0<=c->d-1; i0++)
|
|
{
|
|
t = c->f.ptr.p_double[c->d*(i*c->n+j)+i0];
|
|
c->f.ptr.p_double[c->d*(i*c->n+j)+i0] = c->f.ptr.p_double[c->d*(i*c->n+k)+i0];
|
|
c->f.ptr.p_double[c->d*(i*c->n+k)+i0] = t;
|
|
}
|
|
}
|
|
t = c->x.ptr.p_double[j];
|
|
c->x.ptr.p_double[j] = c->x.ptr.p_double[k];
|
|
c->x.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
k = i;
|
|
for(j=i+1; j<=c->m-1; j++)
|
|
{
|
|
if( ae_fp_less(c->y.ptr.p_double[j],c->y.ptr.p_double[k]) )
|
|
{
|
|
k = j;
|
|
}
|
|
}
|
|
if( k!=i )
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(i0=0; i0<=c->d-1; i0++)
|
|
{
|
|
t = c->f.ptr.p_double[c->d*(i*c->n+j)+i0];
|
|
c->f.ptr.p_double[c->d*(i*c->n+j)+i0] = c->f.ptr.p_double[c->d*(k*c->n+j)+i0];
|
|
c->f.ptr.p_double[c->d*(k*c->n+j)+i0] = t;
|
|
}
|
|
}
|
|
t = c->y.ptr.p_double[i];
|
|
c->y.ptr.p_double[i] = c->y.ptr.p_double[k];
|
|
c->y.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds bicubic vector-valued spline.
|
|
|
|
Input parameters:
|
|
X - spline abscissas, array[0..N-1]
|
|
Y - spline ordinates, array[0..M-1]
|
|
F - function values, array[0..M*N*D-1]:
|
|
* first D elements store D values at (X[0],Y[0])
|
|
* next D elements store D values at (X[1],Y[0])
|
|
* general form - D function values at (X[i],Y[j]) are stored
|
|
at F[D*(J*N+I)...D*(J*N+I)+D-1].
|
|
M,N - grid size, M>=2, N>=2
|
|
D - vector dimension, D>=1
|
|
|
|
Output parameters:
|
|
C - spline interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbicubicv(/* Real */ ae_vector* x,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t m,
|
|
/* Real */ ae_vector* f,
|
|
ae_int_t d,
|
|
spline2dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _f;
|
|
ae_matrix tf;
|
|
ae_matrix dx;
|
|
ae_matrix dy;
|
|
ae_matrix dxy;
|
|
double t;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t di;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init_copy(&_f, f, _state, ae_true);
|
|
f = &_f;
|
|
_spline2dinterpolant_clear(c);
|
|
ae_matrix_init(&tf, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&dx, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&dy, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&dxy, 0, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=2, "Spline2DBuildBicubicV: N is less than 2", _state);
|
|
ae_assert(m>=2, "Spline2DBuildBicubicV: M is less than 2", _state);
|
|
ae_assert(d>=1, "Spline2DBuildBicubicV: invalid argument D (D<1)", _state);
|
|
ae_assert(x->cnt>=n&&y->cnt>=m, "Spline2DBuildBicubicV: length of X or Y is too short (Length(X/Y)<N/M)", _state);
|
|
ae_assert(isfinitevector(x, n, _state)&&isfinitevector(y, m, _state), "Spline2DBuildBicubicV: X or Y contains NaN or Infinite value", _state);
|
|
k = n*m*d;
|
|
ae_assert(f->cnt>=k, "Spline2DBuildBicubicV: length of F is too short (Length(F)<N*M*D)", _state);
|
|
ae_assert(isfinitevector(f, k, _state), "Spline2DBuildBicubicV: F contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Fill interpolant:
|
|
* F[0]...F[N*M*D-1]:
|
|
* f(i,j) table. f(0,0), f(0, 1), f(0,2) and so on...
|
|
* F[N*M*D]...F[2*N*M*D-1]:
|
|
* df(i,j)/dx table.
|
|
* F[2*N*M*D]...F[3*N*M*D-1]:
|
|
* df(i,j)/dy table.
|
|
* F[3*N*M*D]...F[4*N*M*D-1]:
|
|
* d2f(i,j)/dxdy table.
|
|
*/
|
|
c->k = 3;
|
|
c->d = d;
|
|
c->n = n;
|
|
c->m = m;
|
|
c->stype = -3;
|
|
k = 4*k;
|
|
ae_vector_set_length(&c->x, c->n, _state);
|
|
ae_vector_set_length(&c->y, c->m, _state);
|
|
ae_vector_set_length(&c->f, k, _state);
|
|
ae_matrix_set_length(&tf, c->m, c->n, _state);
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
c->x.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
c->y.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Sort points
|
|
*/
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
k = j;
|
|
for(i=j+1; i<=c->n-1; i++)
|
|
{
|
|
if( ae_fp_less(c->x.ptr.p_double[i],c->x.ptr.p_double[k]) )
|
|
{
|
|
k = i;
|
|
}
|
|
}
|
|
if( k!=j )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
t = f->ptr.p_double[c->d*(i*c->n+j)+di];
|
|
f->ptr.p_double[c->d*(i*c->n+j)+di] = f->ptr.p_double[c->d*(i*c->n+k)+di];
|
|
f->ptr.p_double[c->d*(i*c->n+k)+di] = t;
|
|
}
|
|
}
|
|
t = c->x.ptr.p_double[j];
|
|
c->x.ptr.p_double[j] = c->x.ptr.p_double[k];
|
|
c->x.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
k = i;
|
|
for(j=i+1; j<=c->m-1; j++)
|
|
{
|
|
if( ae_fp_less(c->y.ptr.p_double[j],c->y.ptr.p_double[k]) )
|
|
{
|
|
k = j;
|
|
}
|
|
}
|
|
if( k!=i )
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
t = f->ptr.p_double[c->d*(i*c->n+j)+di];
|
|
f->ptr.p_double[c->d*(i*c->n+j)+di] = f->ptr.p_double[c->d*(k*c->n+j)+di];
|
|
f->ptr.p_double[c->d*(k*c->n+j)+di] = t;
|
|
}
|
|
}
|
|
t = c->y.ptr.p_double[i];
|
|
c->y.ptr.p_double[i] = c->y.ptr.p_double[k];
|
|
c->y.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
tf.ptr.pp_double[i][j] = f->ptr.p_double[c->d*(i*c->n+j)+di];
|
|
}
|
|
}
|
|
spline2d_bicubiccalcderivatives(&tf, &c->x, &c->y, c->m, c->n, &dx, &dy, &dxy, _state);
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
k = c->d*(i*c->n+j)+di;
|
|
c->f.ptr.p_double[k] = tf.ptr.pp_double[i][j];
|
|
c->f.ptr.p_double[c->n*c->m*c->d+k] = dx.ptr.pp_double[i][j];
|
|
c->f.ptr.p_double[2*c->n*c->m*c->d+k] = dy.ptr.pp_double[i][j];
|
|
c->f.ptr.p_double[3*c->n*c->m*c->d+k] = dxy.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
F - output buffer, possibly preallocated array. In case array size
|
|
is large enough to store result, it is not reallocated. Array
|
|
which is too short will be reallocated
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] (or larger) which stores function values
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcalcvbuf(spline2dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
/* Real */ ae_vector* f,
|
|
ae_state *_state)
|
|
{
|
|
double t;
|
|
double dt;
|
|
double u;
|
|
double du;
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t h;
|
|
ae_int_t s1;
|
|
ae_int_t s2;
|
|
ae_int_t s3;
|
|
ae_int_t s4;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
double y1;
|
|
double y2;
|
|
double y3;
|
|
double y4;
|
|
double v;
|
|
double t0;
|
|
double t1;
|
|
double t2;
|
|
double t3;
|
|
double u0;
|
|
double u1;
|
|
double u2;
|
|
double u3;
|
|
ae_int_t i;
|
|
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline2DCalcVBuf: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(x, _state)&&ae_isfinite(y, _state), "Spline2DCalcVBuf: either X=NaN/Infinite or Y=NaN/Infinite", _state);
|
|
rvectorsetlengthatleast(f, c->d, _state);
|
|
|
|
/*
|
|
* Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->n-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->x.ptr.p_double[h],x) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
t = (x-c->x.ptr.p_double[l])/(c->x.ptr.p_double[l+1]-c->x.ptr.p_double[l]);
|
|
dt = 1.0/(c->x.ptr.p_double[l+1]-c->x.ptr.p_double[l]);
|
|
ix = l;
|
|
|
|
/*
|
|
* Binary search in the [ y[0], ..., y[m-2] ] (y[m-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->m-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->y.ptr.p_double[h],y) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
u = (y-c->y.ptr.p_double[l])/(c->y.ptr.p_double[l+1]-c->y.ptr.p_double[l]);
|
|
du = 1.0/(c->y.ptr.p_double[l+1]-c->y.ptr.p_double[l]);
|
|
iy = l;
|
|
|
|
/*
|
|
* Bilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
for(i=0; i<=c->d-1; i++)
|
|
{
|
|
y1 = c->f.ptr.p_double[c->d*(c->n*iy+ix)+i];
|
|
y2 = c->f.ptr.p_double[c->d*(c->n*iy+(ix+1))+i];
|
|
y3 = c->f.ptr.p_double[c->d*(c->n*(iy+1)+(ix+1))+i];
|
|
y4 = c->f.ptr.p_double[c->d*(c->n*(iy+1)+ix)+i];
|
|
f->ptr.p_double[i] = (1-t)*(1-u)*y1+t*(1-u)*y2+t*u*y3+(1-t)*u*y4;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Bicubic interpolation
|
|
*/
|
|
if( c->stype==-3 )
|
|
{
|
|
|
|
/*
|
|
* Prepare info
|
|
*/
|
|
t0 = 1;
|
|
t1 = t;
|
|
t2 = ae_sqr(t, _state);
|
|
t3 = t*t2;
|
|
u0 = 1;
|
|
u1 = u;
|
|
u2 = ae_sqr(u, _state);
|
|
u3 = u*u2;
|
|
sfx = c->n*c->m*c->d;
|
|
sfy = 2*c->n*c->m*c->d;
|
|
sfxy = 3*c->n*c->m*c->d;
|
|
for(i=0; i<=c->d-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Prepare F, dF/dX, dF/dY, d2F/dXdY
|
|
*/
|
|
f->ptr.p_double[i] = 0;
|
|
s1 = c->d*(c->n*iy+ix)+i;
|
|
s2 = c->d*(c->n*iy+(ix+1))+i;
|
|
s3 = c->d*(c->n*(iy+1)+(ix+1))+i;
|
|
s4 = c->d*(c->n*(iy+1)+ix)+i;
|
|
|
|
/*
|
|
* Calculate
|
|
*/
|
|
v = c->f.ptr.p_double[s1];
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t0*u0;
|
|
v = c->f.ptr.p_double[sfy+s1]/du;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t0*u1;
|
|
v = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s4]-2*c->f.ptr.p_double[sfy+s1]/du-c->f.ptr.p_double[sfy+s4]/du;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t0*u2;
|
|
v = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s4]+c->f.ptr.p_double[sfy+s1]/du+c->f.ptr.p_double[sfy+s4]/du;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t0*u3;
|
|
v = c->f.ptr.p_double[sfx+s1]/dt;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t1*u0;
|
|
v = c->f.ptr.p_double[sfxy+s1]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t1*u1;
|
|
v = -3*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t1*u2;
|
|
v = 2*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t1*u3;
|
|
v = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s2]-2*c->f.ptr.p_double[sfx+s1]/dt-c->f.ptr.p_double[sfx+s2]/dt;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t2*u0;
|
|
v = -3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t2*u1;
|
|
v = 9*c->f.ptr.p_double[s1]-9*c->f.ptr.p_double[s2]+9*c->f.ptr.p_double[s3]-9*c->f.ptr.p_double[s4]+6*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s2]/dt-3*c->f.ptr.p_double[sfx+s3]/dt-6*c->f.ptr.p_double[sfx+s4]/dt+6*c->f.ptr.p_double[sfy+s1]/du-6*c->f.ptr.p_double[sfy+s2]/du-3*c->f.ptr.p_double[sfy+s3]/du+3*c->f.ptr.p_double[sfy+s4]/du+4*c->f.ptr.p_double[sfxy+s1]/(dt*du)+2*c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t2*u2;
|
|
v = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-4*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s2]/dt+2*c->f.ptr.p_double[sfx+s3]/dt+4*c->f.ptr.p_double[sfx+s4]/dt-3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du+3*c->f.ptr.p_double[sfy+s3]/du-3*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t2*u3;
|
|
v = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s2]+c->f.ptr.p_double[sfx+s1]/dt+c->f.ptr.p_double[sfx+s2]/dt;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t3*u0;
|
|
v = 2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t3*u1;
|
|
v = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-3*c->f.ptr.p_double[sfx+s1]/dt-3*c->f.ptr.p_double[sfx+s2]/dt+3*c->f.ptr.p_double[sfx+s3]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-4*c->f.ptr.p_double[sfy+s1]/du+4*c->f.ptr.p_double[sfy+s2]/du+2*c->f.ptr.p_double[sfy+s3]/du-2*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-2*c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t3*u2;
|
|
v = 4*c->f.ptr.p_double[s1]-4*c->f.ptr.p_double[s2]+4*c->f.ptr.p_double[s3]-4*c->f.ptr.p_double[s4]+2*c->f.ptr.p_double[sfx+s1]/dt+2*c->f.ptr.p_double[sfx+s2]/dt-2*c->f.ptr.p_double[sfx+s3]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfy+s3]/du+2*c->f.ptr.p_double[sfy+s4]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+v*t3*u3;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] which stores function values. F is out-parameter and
|
|
it is reallocated after call to this function. In case you
|
|
want to reuse previously allocated F, you may use
|
|
Spline2DCalcVBuf(), which reallocates F only when it is too
|
|
small.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcalcv(spline2dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
/* Real */ ae_vector* f,
|
|
ae_state *_state)
|
|
{
|
|
|
|
ae_vector_clear(f);
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline2DCalcV: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(x, _state)&&ae_isfinite(y, _state), "Spline2DCalcV: either X=NaN/Infinite or Y=NaN/Infinite", _state);
|
|
ae_vector_set_length(f, c->d, _state);
|
|
spline2dcalcvbuf(c, x, y, f, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine unpacks two-dimensional spline into the coefficients table
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
|
|
Result:
|
|
M, N- grid size (x-axis and y-axis)
|
|
D - number of components
|
|
Tbl - coefficients table, unpacked format,
|
|
D - components: [0..(N-1)*(M-1)*D-1, 0..19].
|
|
For T=0..D-1 (component index), I = 0...N-2 (x index),
|
|
J=0..M-2 (y index):
|
|
K := T + I*D + J*D*(N-1)
|
|
|
|
K-th row stores decomposition for T-th component of the
|
|
vector-valued function
|
|
|
|
Tbl[K,0] = X[i]
|
|
Tbl[K,1] = X[i+1]
|
|
Tbl[K,2] = Y[j]
|
|
Tbl[K,3] = Y[j+1]
|
|
Tbl[K,4] = C00
|
|
Tbl[K,5] = C01
|
|
Tbl[K,6] = C02
|
|
Tbl[K,7] = C03
|
|
Tbl[K,8] = C10
|
|
Tbl[K,9] = C11
|
|
...
|
|
Tbl[K,19] = C33
|
|
On each grid square spline is equals to:
|
|
S(x) = SUM(c[i,j]*(t^i)*(u^j), i=0..3, j=0..3)
|
|
t = x-x[j]
|
|
u = y-y[i]
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 16.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dunpackv(spline2dinterpolant* c,
|
|
ae_int_t* m,
|
|
ae_int_t* n,
|
|
ae_int_t* d,
|
|
/* Real */ ae_matrix* tbl,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t k;
|
|
ae_int_t p;
|
|
ae_int_t ci;
|
|
ae_int_t cj;
|
|
ae_int_t s1;
|
|
ae_int_t s2;
|
|
ae_int_t s3;
|
|
ae_int_t s4;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
double y1;
|
|
double y2;
|
|
double y3;
|
|
double y4;
|
|
double dt;
|
|
double du;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k0;
|
|
|
|
*m = 0;
|
|
*n = 0;
|
|
*d = 0;
|
|
ae_matrix_clear(tbl);
|
|
|
|
ae_assert(c->stype==-3||c->stype==-1, "Spline2DUnpackV: incorrect C (incorrect parameter C.SType)", _state);
|
|
*n = c->n;
|
|
*m = c->m;
|
|
*d = c->d;
|
|
ae_matrix_set_length(tbl, (*n-1)*(*m-1)*(*d), 20, _state);
|
|
sfx = *n*(*m)*(*d);
|
|
sfy = 2*(*n)*(*m)*(*d);
|
|
sfxy = 3*(*n)*(*m)*(*d);
|
|
for(i=0; i<=*m-2; i++)
|
|
{
|
|
for(j=0; j<=*n-2; j++)
|
|
{
|
|
for(k=0; k<=*d-1; k++)
|
|
{
|
|
p = *d*(i*(*n-1)+j)+k;
|
|
tbl->ptr.pp_double[p][0] = c->x.ptr.p_double[j];
|
|
tbl->ptr.pp_double[p][1] = c->x.ptr.p_double[j+1];
|
|
tbl->ptr.pp_double[p][2] = c->y.ptr.p_double[i];
|
|
tbl->ptr.pp_double[p][3] = c->y.ptr.p_double[i+1];
|
|
dt = 1/(tbl->ptr.pp_double[p][1]-tbl->ptr.pp_double[p][0]);
|
|
du = 1/(tbl->ptr.pp_double[p][3]-tbl->ptr.pp_double[p][2]);
|
|
|
|
/*
|
|
* Bilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
for(k0=4; k0<=19; k0++)
|
|
{
|
|
tbl->ptr.pp_double[p][k0] = 0;
|
|
}
|
|
y1 = c->f.ptr.p_double[*d*(*n*i+j)+k];
|
|
y2 = c->f.ptr.p_double[*d*(*n*i+(j+1))+k];
|
|
y3 = c->f.ptr.p_double[*d*(*n*(i+1)+(j+1))+k];
|
|
y4 = c->f.ptr.p_double[*d*(*n*(i+1)+j)+k];
|
|
tbl->ptr.pp_double[p][4] = y1;
|
|
tbl->ptr.pp_double[p][4+1*4+0] = y2-y1;
|
|
tbl->ptr.pp_double[p][4+0*4+1] = y4-y1;
|
|
tbl->ptr.pp_double[p][4+1*4+1] = y3-y2-y4+y1;
|
|
}
|
|
|
|
/*
|
|
* Bicubic interpolation
|
|
*/
|
|
if( c->stype==-3 )
|
|
{
|
|
s1 = *d*(*n*i+j)+k;
|
|
s2 = *d*(*n*i+(j+1))+k;
|
|
s3 = *d*(*n*(i+1)+(j+1))+k;
|
|
s4 = *d*(*n*(i+1)+j)+k;
|
|
tbl->ptr.pp_double[p][4+0*4+0] = c->f.ptr.p_double[s1];
|
|
tbl->ptr.pp_double[p][4+0*4+1] = c->f.ptr.p_double[sfy+s1]/du;
|
|
tbl->ptr.pp_double[p][4+0*4+2] = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s4]-2*c->f.ptr.p_double[sfy+s1]/du-c->f.ptr.p_double[sfy+s4]/du;
|
|
tbl->ptr.pp_double[p][4+0*4+3] = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s4]+c->f.ptr.p_double[sfy+s1]/du+c->f.ptr.p_double[sfy+s4]/du;
|
|
tbl->ptr.pp_double[p][4+1*4+0] = c->f.ptr.p_double[sfx+s1]/dt;
|
|
tbl->ptr.pp_double[p][4+1*4+1] = c->f.ptr.p_double[sfxy+s1]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+1*4+2] = -3*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+1*4+3] = 2*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+2*4+0] = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s2]-2*c->f.ptr.p_double[sfx+s1]/dt-c->f.ptr.p_double[sfx+s2]/dt;
|
|
tbl->ptr.pp_double[p][4+2*4+1] = -3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+2*4+2] = 9*c->f.ptr.p_double[s1]-9*c->f.ptr.p_double[s2]+9*c->f.ptr.p_double[s3]-9*c->f.ptr.p_double[s4]+6*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s2]/dt-3*c->f.ptr.p_double[sfx+s3]/dt-6*c->f.ptr.p_double[sfx+s4]/dt+6*c->f.ptr.p_double[sfy+s1]/du-6*c->f.ptr.p_double[sfy+s2]/du-3*c->f.ptr.p_double[sfy+s3]/du+3*c->f.ptr.p_double[sfy+s4]/du+4*c->f.ptr.p_double[sfxy+s1]/(dt*du)+2*c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+2*4+3] = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-4*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s2]/dt+2*c->f.ptr.p_double[sfx+s3]/dt+4*c->f.ptr.p_double[sfx+s4]/dt-3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du+3*c->f.ptr.p_double[sfy+s3]/du-3*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+3*4+0] = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s2]+c->f.ptr.p_double[sfx+s1]/dt+c->f.ptr.p_double[sfx+s2]/dt;
|
|
tbl->ptr.pp_double[p][4+3*4+1] = 2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+3*4+2] = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-3*c->f.ptr.p_double[sfx+s1]/dt-3*c->f.ptr.p_double[sfx+s2]/dt+3*c->f.ptr.p_double[sfx+s3]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-4*c->f.ptr.p_double[sfy+s1]/du+4*c->f.ptr.p_double[sfy+s2]/du+2*c->f.ptr.p_double[sfy+s3]/du-2*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-2*c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+3*4+3] = 4*c->f.ptr.p_double[s1]-4*c->f.ptr.p_double[s2]+4*c->f.ptr.p_double[s3]-4*c->f.ptr.p_double[s4]+2*c->f.ptr.p_double[sfx+s1]/dt+2*c->f.ptr.p_double[sfx+s2]/dt-2*c->f.ptr.p_double[sfx+s3]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfy+s3]/du+2*c->f.ptr.p_double[sfy+s4]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
}
|
|
|
|
/*
|
|
* Rescale Cij
|
|
*/
|
|
for(ci=0; ci<=3; ci++)
|
|
{
|
|
for(cj=0; cj<=3; cj++)
|
|
{
|
|
tbl->ptr.pp_double[p][4+ci*4+cj] = tbl->ptr.pp_double[p][4+ci*4+cj]*ae_pow(dt, ci, _state)*ae_pow(du, cj, _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine was deprecated in ALGLIB 3.6.0
|
|
|
|
We recommend you to switch to Spline2DBuildBilinearV(), which is more
|
|
flexible and accepts its arguments in more convenient order.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbilinear(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_matrix* f,
|
|
ae_int_t m,
|
|
ae_int_t n,
|
|
spline2dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
double t;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
|
|
_spline2dinterpolant_clear(c);
|
|
|
|
ae_assert(n>=2, "Spline2DBuildBilinear: N<2", _state);
|
|
ae_assert(m>=2, "Spline2DBuildBilinear: M<2", _state);
|
|
ae_assert(x->cnt>=n&&y->cnt>=m, "Spline2DBuildBilinear: length of X or Y is too short (Length(X/Y)<N/M)", _state);
|
|
ae_assert(isfinitevector(x, n, _state)&&isfinitevector(y, m, _state), "Spline2DBuildBilinear: X or Y contains NaN or Infinite value", _state);
|
|
ae_assert(f->rows>=m&&f->cols>=n, "Spline2DBuildBilinear: size of F is too small (rows(F)<M or cols(F)<N)", _state);
|
|
ae_assert(apservisfinitematrix(f, m, n, _state), "Spline2DBuildBilinear: F contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Fill interpolant
|
|
*/
|
|
c->k = 1;
|
|
c->n = n;
|
|
c->m = m;
|
|
c->d = 1;
|
|
c->stype = -1;
|
|
ae_vector_set_length(&c->x, c->n, _state);
|
|
ae_vector_set_length(&c->y, c->m, _state);
|
|
ae_vector_set_length(&c->f, c->n*c->m, _state);
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
c->x.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
c->y.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
c->f.ptr.p_double[i*c->n+j] = f->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sort points
|
|
*/
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
k = j;
|
|
for(i=j+1; i<=c->n-1; i++)
|
|
{
|
|
if( ae_fp_less(c->x.ptr.p_double[i],c->x.ptr.p_double[k]) )
|
|
{
|
|
k = i;
|
|
}
|
|
}
|
|
if( k!=j )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
t = c->f.ptr.p_double[i*c->n+j];
|
|
c->f.ptr.p_double[i*c->n+j] = c->f.ptr.p_double[i*c->n+k];
|
|
c->f.ptr.p_double[i*c->n+k] = t;
|
|
}
|
|
t = c->x.ptr.p_double[j];
|
|
c->x.ptr.p_double[j] = c->x.ptr.p_double[k];
|
|
c->x.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
k = i;
|
|
for(j=i+1; j<=c->m-1; j++)
|
|
{
|
|
if( ae_fp_less(c->y.ptr.p_double[j],c->y.ptr.p_double[k]) )
|
|
{
|
|
k = j;
|
|
}
|
|
}
|
|
if( k!=i )
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
t = c->f.ptr.p_double[i*c->n+j];
|
|
c->f.ptr.p_double[i*c->n+j] = c->f.ptr.p_double[k*c->n+j];
|
|
c->f.ptr.p_double[k*c->n+j] = t;
|
|
}
|
|
t = c->y.ptr.p_double[i];
|
|
c->y.ptr.p_double[i] = c->y.ptr.p_double[k];
|
|
c->y.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine was deprecated in ALGLIB 3.6.0
|
|
|
|
We recommend you to switch to Spline2DBuildBicubicV(), which is more
|
|
flexible and accepts its arguments in more convenient order.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 05.07.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildbicubic(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
/* Real */ ae_matrix* f,
|
|
ae_int_t m,
|
|
ae_int_t n,
|
|
spline2dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_matrix _f;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
ae_matrix dx;
|
|
ae_matrix dy;
|
|
ae_matrix dxy;
|
|
double t;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_init_copy(&_f, f, _state, ae_true);
|
|
f = &_f;
|
|
_spline2dinterpolant_clear(c);
|
|
ae_matrix_init(&dx, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&dy, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&dxy, 0, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=2, "Spline2DBuildBicubicSpline: N<2", _state);
|
|
ae_assert(m>=2, "Spline2DBuildBicubicSpline: M<2", _state);
|
|
ae_assert(x->cnt>=n&&y->cnt>=m, "Spline2DBuildBicubic: length of X or Y is too short (Length(X/Y)<N/M)", _state);
|
|
ae_assert(isfinitevector(x, n, _state)&&isfinitevector(y, m, _state), "Spline2DBuildBicubic: X or Y contains NaN or Infinite value", _state);
|
|
ae_assert(f->rows>=m&&f->cols>=n, "Spline2DBuildBicubic: size of F is too small (rows(F)<M or cols(F)<N)", _state);
|
|
ae_assert(apservisfinitematrix(f, m, n, _state), "Spline2DBuildBicubic: F contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Fill interpolant:
|
|
* F[0]...F[N*M-1]:
|
|
* f(i,j) table. f(0,0), f(0, 1), f(0,2) and so on...
|
|
* F[N*M]...F[2*N*M-1]:
|
|
* df(i,j)/dx table.
|
|
* F[2*N*M]...F[3*N*M-1]:
|
|
* df(i,j)/dy table.
|
|
* F[3*N*M]...F[4*N*M-1]:
|
|
* d2f(i,j)/dxdy table.
|
|
*/
|
|
c->k = 3;
|
|
c->d = 1;
|
|
c->n = n;
|
|
c->m = m;
|
|
c->stype = -3;
|
|
sfx = c->n*c->m;
|
|
sfy = 2*c->n*c->m;
|
|
sfxy = 3*c->n*c->m;
|
|
ae_vector_set_length(&c->x, c->n, _state);
|
|
ae_vector_set_length(&c->y, c->m, _state);
|
|
ae_vector_set_length(&c->f, 4*c->n*c->m, _state);
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
c->x.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
c->y.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Sort points
|
|
*/
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
k = j;
|
|
for(i=j+1; i<=c->n-1; i++)
|
|
{
|
|
if( ae_fp_less(c->x.ptr.p_double[i],c->x.ptr.p_double[k]) )
|
|
{
|
|
k = i;
|
|
}
|
|
}
|
|
if( k!=j )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
t = f->ptr.pp_double[i][j];
|
|
f->ptr.pp_double[i][j] = f->ptr.pp_double[i][k];
|
|
f->ptr.pp_double[i][k] = t;
|
|
}
|
|
t = c->x.ptr.p_double[j];
|
|
c->x.ptr.p_double[j] = c->x.ptr.p_double[k];
|
|
c->x.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
k = i;
|
|
for(j=i+1; j<=c->m-1; j++)
|
|
{
|
|
if( ae_fp_less(c->y.ptr.p_double[j],c->y.ptr.p_double[k]) )
|
|
{
|
|
k = j;
|
|
}
|
|
}
|
|
if( k!=i )
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
t = f->ptr.pp_double[i][j];
|
|
f->ptr.pp_double[i][j] = f->ptr.pp_double[k][j];
|
|
f->ptr.pp_double[k][j] = t;
|
|
}
|
|
t = c->y.ptr.p_double[i];
|
|
c->y.ptr.p_double[i] = c->y.ptr.p_double[k];
|
|
c->y.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
spline2d_bicubiccalcderivatives(f, &c->x, &c->y, c->m, c->n, &dx, &dy, &dxy, _state);
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
k = i*c->n+j;
|
|
c->f.ptr.p_double[k] = f->ptr.pp_double[i][j];
|
|
c->f.ptr.p_double[sfx+k] = dx.ptr.pp_double[i][j];
|
|
c->f.ptr.p_double[sfy+k] = dy.ptr.pp_double[i][j];
|
|
c->f.ptr.p_double[sfxy+k] = dxy.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine was deprecated in ALGLIB 3.6.0
|
|
|
|
We recommend you to switch to Spline2DUnpackV(), which is more flexible
|
|
and accepts its arguments in more convenient order.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.06.2007 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dunpack(spline2dinterpolant* c,
|
|
ae_int_t* m,
|
|
ae_int_t* n,
|
|
/* Real */ ae_matrix* tbl,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t k;
|
|
ae_int_t p;
|
|
ae_int_t ci;
|
|
ae_int_t cj;
|
|
ae_int_t s1;
|
|
ae_int_t s2;
|
|
ae_int_t s3;
|
|
ae_int_t s4;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
double y1;
|
|
double y2;
|
|
double y3;
|
|
double y4;
|
|
double dt;
|
|
double du;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
*m = 0;
|
|
*n = 0;
|
|
ae_matrix_clear(tbl);
|
|
|
|
ae_assert(c->stype==-3||c->stype==-1, "Spline2DUnpack: incorrect C (incorrect parameter C.SType)", _state);
|
|
if( c->d!=1 )
|
|
{
|
|
*n = 0;
|
|
*m = 0;
|
|
return;
|
|
}
|
|
*n = c->n;
|
|
*m = c->m;
|
|
ae_matrix_set_length(tbl, (*n-1)*(*m-1), 20, _state);
|
|
sfx = *n*(*m);
|
|
sfy = 2*(*n)*(*m);
|
|
sfxy = 3*(*n)*(*m);
|
|
|
|
/*
|
|
* Fill
|
|
*/
|
|
for(i=0; i<=*m-2; i++)
|
|
{
|
|
for(j=0; j<=*n-2; j++)
|
|
{
|
|
p = i*(*n-1)+j;
|
|
tbl->ptr.pp_double[p][0] = c->x.ptr.p_double[j];
|
|
tbl->ptr.pp_double[p][1] = c->x.ptr.p_double[j+1];
|
|
tbl->ptr.pp_double[p][2] = c->y.ptr.p_double[i];
|
|
tbl->ptr.pp_double[p][3] = c->y.ptr.p_double[i+1];
|
|
dt = 1/(tbl->ptr.pp_double[p][1]-tbl->ptr.pp_double[p][0]);
|
|
du = 1/(tbl->ptr.pp_double[p][3]-tbl->ptr.pp_double[p][2]);
|
|
|
|
/*
|
|
* Bilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
for(k=4; k<=19; k++)
|
|
{
|
|
tbl->ptr.pp_double[p][k] = 0;
|
|
}
|
|
y1 = c->f.ptr.p_double[*n*i+j];
|
|
y2 = c->f.ptr.p_double[*n*i+(j+1)];
|
|
y3 = c->f.ptr.p_double[*n*(i+1)+(j+1)];
|
|
y4 = c->f.ptr.p_double[*n*(i+1)+j];
|
|
tbl->ptr.pp_double[p][4] = y1;
|
|
tbl->ptr.pp_double[p][4+1*4+0] = y2-y1;
|
|
tbl->ptr.pp_double[p][4+0*4+1] = y4-y1;
|
|
tbl->ptr.pp_double[p][4+1*4+1] = y3-y2-y4+y1;
|
|
}
|
|
|
|
/*
|
|
* Bicubic interpolation
|
|
*/
|
|
if( c->stype==-3 )
|
|
{
|
|
s1 = *n*i+j;
|
|
s2 = *n*i+(j+1);
|
|
s3 = *n*(i+1)+(j+1);
|
|
s4 = *n*(i+1)+j;
|
|
tbl->ptr.pp_double[p][4+0*4+0] = c->f.ptr.p_double[s1];
|
|
tbl->ptr.pp_double[p][4+0*4+1] = c->f.ptr.p_double[sfy+s1]/du;
|
|
tbl->ptr.pp_double[p][4+0*4+2] = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s4]-2*c->f.ptr.p_double[sfy+s1]/du-c->f.ptr.p_double[sfy+s4]/du;
|
|
tbl->ptr.pp_double[p][4+0*4+3] = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s4]+c->f.ptr.p_double[sfy+s1]/du+c->f.ptr.p_double[sfy+s4]/du;
|
|
tbl->ptr.pp_double[p][4+1*4+0] = c->f.ptr.p_double[sfx+s1]/dt;
|
|
tbl->ptr.pp_double[p][4+1*4+1] = c->f.ptr.p_double[sfxy+s1]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+1*4+2] = -3*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+1*4+3] = 2*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+2*4+0] = -3*c->f.ptr.p_double[s1]+3*c->f.ptr.p_double[s2]-2*c->f.ptr.p_double[sfx+s1]/dt-c->f.ptr.p_double[sfx+s2]/dt;
|
|
tbl->ptr.pp_double[p][4+2*4+1] = -3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+2*4+2] = 9*c->f.ptr.p_double[s1]-9*c->f.ptr.p_double[s2]+9*c->f.ptr.p_double[s3]-9*c->f.ptr.p_double[s4]+6*c->f.ptr.p_double[sfx+s1]/dt+3*c->f.ptr.p_double[sfx+s2]/dt-3*c->f.ptr.p_double[sfx+s3]/dt-6*c->f.ptr.p_double[sfx+s4]/dt+6*c->f.ptr.p_double[sfy+s1]/du-6*c->f.ptr.p_double[sfy+s2]/du-3*c->f.ptr.p_double[sfy+s3]/du+3*c->f.ptr.p_double[sfy+s4]/du+4*c->f.ptr.p_double[sfxy+s1]/(dt*du)+2*c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+2*4+3] = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-4*c->f.ptr.p_double[sfx+s1]/dt-2*c->f.ptr.p_double[sfx+s2]/dt+2*c->f.ptr.p_double[sfx+s3]/dt+4*c->f.ptr.p_double[sfx+s4]/dt-3*c->f.ptr.p_double[sfy+s1]/du+3*c->f.ptr.p_double[sfy+s2]/du+3*c->f.ptr.p_double[sfy+s3]/du-3*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-2*c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+3*4+0] = 2*c->f.ptr.p_double[s1]-2*c->f.ptr.p_double[s2]+c->f.ptr.p_double[sfx+s1]/dt+c->f.ptr.p_double[sfx+s2]/dt;
|
|
tbl->ptr.pp_double[p][4+3*4+1] = 2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+3*4+2] = -6*c->f.ptr.p_double[s1]+6*c->f.ptr.p_double[s2]-6*c->f.ptr.p_double[s3]+6*c->f.ptr.p_double[s4]-3*c->f.ptr.p_double[sfx+s1]/dt-3*c->f.ptr.p_double[sfx+s2]/dt+3*c->f.ptr.p_double[sfx+s3]/dt+3*c->f.ptr.p_double[sfx+s4]/dt-4*c->f.ptr.p_double[sfy+s1]/du+4*c->f.ptr.p_double[sfy+s2]/du+2*c->f.ptr.p_double[sfy+s3]/du-2*c->f.ptr.p_double[sfy+s4]/du-2*c->f.ptr.p_double[sfxy+s1]/(dt*du)-2*c->f.ptr.p_double[sfxy+s2]/(dt*du)-c->f.ptr.p_double[sfxy+s3]/(dt*du)-c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
tbl->ptr.pp_double[p][4+3*4+3] = 4*c->f.ptr.p_double[s1]-4*c->f.ptr.p_double[s2]+4*c->f.ptr.p_double[s3]-4*c->f.ptr.p_double[s4]+2*c->f.ptr.p_double[sfx+s1]/dt+2*c->f.ptr.p_double[sfx+s2]/dt-2*c->f.ptr.p_double[sfx+s3]/dt-2*c->f.ptr.p_double[sfx+s4]/dt+2*c->f.ptr.p_double[sfy+s1]/du-2*c->f.ptr.p_double[sfy+s2]/du-2*c->f.ptr.p_double[sfy+s3]/du+2*c->f.ptr.p_double[sfy+s4]/du+c->f.ptr.p_double[sfxy+s1]/(dt*du)+c->f.ptr.p_double[sfxy+s2]/(dt*du)+c->f.ptr.p_double[sfxy+s3]/(dt*du)+c->f.ptr.p_double[sfxy+s4]/(dt*du);
|
|
}
|
|
|
|
/*
|
|
* Rescale Cij
|
|
*/
|
|
for(ci=0; ci<=3; ci++)
|
|
{
|
|
for(cj=0; cj<=3; cj++)
|
|
{
|
|
tbl->ptr.pp_double[p][4+ci*4+cj] = tbl->ptr.pp_double[p][4+ci*4+cj]*ae_pow(dt, ci, _state)*ae_pow(du, cj, _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal subroutine.
|
|
Calculation of the first derivatives and the cross-derivative.
|
|
*************************************************************************/
|
|
static void spline2d_bicubiccalcderivatives(/* Real */ ae_matrix* a,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t m,
|
|
ae_int_t n,
|
|
/* Real */ ae_matrix* dx,
|
|
/* Real */ ae_matrix* dy,
|
|
/* Real */ ae_matrix* dxy,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_vector xt;
|
|
ae_vector ft;
|
|
double s;
|
|
double ds;
|
|
double d2s;
|
|
spline1dinterpolant c;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_matrix_clear(dx);
|
|
ae_matrix_clear(dy);
|
|
ae_matrix_clear(dxy);
|
|
ae_vector_init(&xt, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ft, 0, DT_REAL, _state, ae_true);
|
|
_spline1dinterpolant_init(&c, _state, ae_true);
|
|
|
|
ae_matrix_set_length(dx, m, n, _state);
|
|
ae_matrix_set_length(dy, m, n, _state);
|
|
ae_matrix_set_length(dxy, m, n, _state);
|
|
|
|
/*
|
|
* dF/dX
|
|
*/
|
|
ae_vector_set_length(&xt, n, _state);
|
|
ae_vector_set_length(&ft, n, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
xt.ptr.p_double[j] = x->ptr.p_double[j];
|
|
ft.ptr.p_double[j] = a->ptr.pp_double[i][j];
|
|
}
|
|
spline1dbuildcubic(&xt, &ft, n, 0, 0.0, 0, 0.0, &c, _state);
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
spline1ddiff(&c, x->ptr.p_double[j], &s, &ds, &d2s, _state);
|
|
dx->ptr.pp_double[i][j] = ds;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* dF/dY
|
|
*/
|
|
ae_vector_set_length(&xt, m, _state);
|
|
ae_vector_set_length(&ft, m, _state);
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
xt.ptr.p_double[i] = y->ptr.p_double[i];
|
|
ft.ptr.p_double[i] = a->ptr.pp_double[i][j];
|
|
}
|
|
spline1dbuildcubic(&xt, &ft, m, 0, 0.0, 0, 0.0, &c, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
spline1ddiff(&c, y->ptr.p_double[i], &s, &ds, &d2s, _state);
|
|
dy->ptr.pp_double[i][j] = ds;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* d2F/dXdY
|
|
*/
|
|
ae_vector_set_length(&xt, n, _state);
|
|
ae_vector_set_length(&ft, n, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
xt.ptr.p_double[j] = x->ptr.p_double[j];
|
|
ft.ptr.p_double[j] = dy->ptr.pp_double[i][j];
|
|
}
|
|
spline1dbuildcubic(&xt, &ft, n, 0, 0.0, 0, 0.0, &c, _state);
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
spline1ddiff(&c, x->ptr.p_double[j], &s, &ds, &d2s, _state);
|
|
dxy->ptr.pp_double[i][j] = ds;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
ae_bool _spline2dinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dinterpolant *p = (spline2dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->f, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _spline2dinterpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dinterpolant *dst = (spline2dinterpolant*)_dst;
|
|
spline2dinterpolant *src = (spline2dinterpolant*)_src;
|
|
dst->k = src->k;
|
|
dst->stype = src->stype;
|
|
dst->n = src->n;
|
|
dst->m = src->m;
|
|
dst->d = src->d;
|
|
if( !ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->f, &src->f, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _spline2dinterpolant_clear(void* _p)
|
|
{
|
|
spline2dinterpolant *p = (spline2dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->x);
|
|
ae_vector_clear(&p->y);
|
|
ae_vector_clear(&p->f);
|
|
}
|
|
|
|
|
|
void _spline2dinterpolant_destroy(void* _p)
|
|
{
|
|
spline2dinterpolant *p = (spline2dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->x);
|
|
ae_vector_destroy(&p->y);
|
|
ae_vector_destroy(&p->f);
|
|
}
|
|
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the trilinear or tricubic spline at
|
|
the given point (X,Y,Z).
|
|
|
|
INPUT PARAMETERS:
|
|
C - coefficients table.
|
|
Built by BuildBilinearSpline or BuildBicubicSpline.
|
|
X, Y,
|
|
Z - point
|
|
|
|
Result:
|
|
S(x,y,z)
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline3dcalc(spline3dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
double z,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
double vx;
|
|
double vy;
|
|
double vxy;
|
|
double result;
|
|
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline3DCalc: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert((ae_isfinite(x, _state)&&ae_isfinite(y, _state))&&ae_isfinite(z, _state), "Spline3DCalc: X=NaN/Infinite, Y=NaN/Infinite or Z=NaN/Infinite", _state);
|
|
if( c->d!=1 )
|
|
{
|
|
result = 0;
|
|
return result;
|
|
}
|
|
spline3d_spline3ddiff(c, x, y, z, &v, &vx, &vy, &vxy, _state);
|
|
result = v;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline argument.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant
|
|
AX, BX - transformation coefficients: x = A*u + B
|
|
AY, BY - transformation coefficients: y = A*v + B
|
|
AZ, BZ - transformation coefficients: z = A*w + B
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dlintransxyz(spline3dinterpolant* c,
|
|
double ax,
|
|
double bx,
|
|
double ay,
|
|
double by,
|
|
double az,
|
|
double bz,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector x;
|
|
ae_vector y;
|
|
ae_vector z;
|
|
ae_vector f;
|
|
ae_vector v;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t di;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&f, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&v, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(c->stype==-3||c->stype==-1, "Spline3DLinTransXYZ: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_vector_set_length(&x, c->n, _state);
|
|
ae_vector_set_length(&y, c->m, _state);
|
|
ae_vector_set_length(&z, c->l, _state);
|
|
ae_vector_set_length(&f, c->m*c->n*c->l*c->d, _state);
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
x.ptr.p_double[j] = c->x.ptr.p_double[j];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
y.ptr.p_double[i] = c->y.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->l-1; i++)
|
|
{
|
|
z.ptr.p_double[i] = c->z.ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Handle different combinations of zero/nonzero AX/AY/AZ
|
|
*/
|
|
if( (ae_fp_neq(ax,0)&&ae_fp_neq(ay,0))&&ae_fp_neq(az,0) )
|
|
{
|
|
ae_v_move(&f.ptr.p_double[0], 1, &c->f.ptr.p_double[0], 1, ae_v_len(0,c->m*c->n*c->l*c->d-1));
|
|
}
|
|
if( (ae_fp_eq(ax,0)&&ae_fp_neq(ay,0))&&ae_fp_neq(az,0) )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j=0; j<=c->l-1; j++)
|
|
{
|
|
spline3dcalcv(c, bx, y.ptr.p_double[i], z.ptr.p_double[j], &v, _state);
|
|
for(k=0; k<=c->n-1; k++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
f.ptr.p_double[c->d*(c->n*(c->m*j+i)+k)+di] = v.ptr.p_double[di];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ax = 1;
|
|
bx = 0;
|
|
}
|
|
if( (ae_fp_neq(ax,0)&&ae_fp_eq(ay,0))&&ae_fp_neq(az,0) )
|
|
{
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
for(j=0; j<=c->l-1; j++)
|
|
{
|
|
spline3dcalcv(c, x.ptr.p_double[i], by, z.ptr.p_double[j], &v, _state);
|
|
for(k=0; k<=c->m-1; k++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
f.ptr.p_double[c->d*(c->n*(c->m*j+k)+i)+di] = v.ptr.p_double[di];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ay = 1;
|
|
by = 0;
|
|
}
|
|
if( (ae_fp_neq(ax,0)&&ae_fp_neq(ay,0))&&ae_fp_eq(az,0) )
|
|
{
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
for(j=0; j<=c->m-1; j++)
|
|
{
|
|
spline3dcalcv(c, x.ptr.p_double[i], y.ptr.p_double[j], bz, &v, _state);
|
|
for(k=0; k<=c->l-1; k++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
f.ptr.p_double[c->d*(c->n*(c->m*k+j)+i)+di] = v.ptr.p_double[di];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
az = 1;
|
|
bz = 0;
|
|
}
|
|
if( (ae_fp_eq(ax,0)&&ae_fp_eq(ay,0))&&ae_fp_neq(az,0) )
|
|
{
|
|
for(i=0; i<=c->l-1; i++)
|
|
{
|
|
spline3dcalcv(c, bx, by, z.ptr.p_double[i], &v, _state);
|
|
for(k=0; k<=c->m-1; k++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
f.ptr.p_double[c->d*(c->n*(c->m*i+k)+j)+di] = v.ptr.p_double[di];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ax = 1;
|
|
bx = 0;
|
|
ay = 1;
|
|
by = 0;
|
|
}
|
|
if( (ae_fp_eq(ax,0)&&ae_fp_neq(ay,0))&&ae_fp_eq(az,0) )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
spline3dcalcv(c, bx, y.ptr.p_double[i], bz, &v, _state);
|
|
for(k=0; k<=c->l-1; k++)
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
f.ptr.p_double[c->d*(c->n*(c->m*k+i)+j)+di] = v.ptr.p_double[di];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ax = 1;
|
|
bx = 0;
|
|
az = 1;
|
|
bz = 0;
|
|
}
|
|
if( (ae_fp_neq(ax,0)&&ae_fp_eq(ay,0))&&ae_fp_eq(az,0) )
|
|
{
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
spline3dcalcv(c, x.ptr.p_double[i], by, bz, &v, _state);
|
|
for(k=0; k<=c->l-1; k++)
|
|
{
|
|
for(j=0; j<=c->m-1; j++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
f.ptr.p_double[c->d*(c->n*(c->m*k+j)+i)+di] = v.ptr.p_double[di];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ay = 1;
|
|
by = 0;
|
|
az = 1;
|
|
bz = 0;
|
|
}
|
|
if( (ae_fp_eq(ax,0)&&ae_fp_eq(ay,0))&&ae_fp_eq(az,0) )
|
|
{
|
|
spline3dcalcv(c, bx, by, bz, &v, _state);
|
|
for(k=0; k<=c->l-1; k++)
|
|
{
|
|
for(j=0; j<=c->m-1; j++)
|
|
{
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
for(di=0; di<=c->d-1; di++)
|
|
{
|
|
f.ptr.p_double[c->d*(c->n*(c->m*k+j)+i)+di] = v.ptr.p_double[di];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ax = 1;
|
|
bx = 0;
|
|
ay = 1;
|
|
by = 0;
|
|
az = 1;
|
|
bz = 0;
|
|
}
|
|
|
|
/*
|
|
* General case: AX<>0, AY<>0, AZ<>0
|
|
* Unpack, scale and pack again.
|
|
*/
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
x.ptr.p_double[i] = (x.ptr.p_double[i]-bx)/ax;
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
y.ptr.p_double[i] = (y.ptr.p_double[i]-by)/ay;
|
|
}
|
|
for(i=0; i<=c->l-1; i++)
|
|
{
|
|
z.ptr.p_double[i] = (z.ptr.p_double[i]-bz)/az;
|
|
}
|
|
if( c->stype==-1 )
|
|
{
|
|
spline3dbuildtrilinearv(&x, c->n, &y, c->m, &z, c->l, &f, c->d, c, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine performs linear transformation of the spline.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
A, B- transformation coefficients: S2(x,y) = A*S(x,y,z) + B
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - transformed spline
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dlintransf(spline3dinterpolant* c,
|
|
double a,
|
|
double b,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector x;
|
|
ae_vector y;
|
|
ae_vector z;
|
|
ae_vector f;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
ae_vector_init(&x, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&y, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&f, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(c->stype==-3||c->stype==-1, "Spline3DLinTransF: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_vector_set_length(&x, c->n, _state);
|
|
ae_vector_set_length(&y, c->m, _state);
|
|
ae_vector_set_length(&z, c->l, _state);
|
|
ae_vector_set_length(&f, c->m*c->n*c->l*c->d, _state);
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
x.ptr.p_double[j] = c->x.ptr.p_double[j];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
y.ptr.p_double[i] = c->y.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->l-1; i++)
|
|
{
|
|
z.ptr.p_double[i] = c->z.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m*c->n*c->l*c->d-1; i++)
|
|
{
|
|
f.ptr.p_double[i] = a*c->f.ptr.p_double[i]+b;
|
|
}
|
|
if( c->stype==-1 )
|
|
{
|
|
spline3dbuildtrilinearv(&x, c->n, &y, c->m, &z, c->l, &f, c->d, c, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine makes the copy of the spline model.
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
OUTPUT PARAMETERS:
|
|
CC - spline copy
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dcopy(spline3dinterpolant* c,
|
|
spline3dinterpolant* cc,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t tblsize;
|
|
|
|
_spline3dinterpolant_clear(cc);
|
|
|
|
ae_assert(c->k==1||c->k==3, "Spline3DCopy: incorrect C (incorrect parameter C.K)", _state);
|
|
cc->k = c->k;
|
|
cc->n = c->n;
|
|
cc->m = c->m;
|
|
cc->l = c->l;
|
|
cc->d = c->d;
|
|
tblsize = c->n*c->m*c->l*c->d;
|
|
cc->stype = c->stype;
|
|
ae_vector_set_length(&cc->x, cc->n, _state);
|
|
ae_vector_set_length(&cc->y, cc->m, _state);
|
|
ae_vector_set_length(&cc->z, cc->l, _state);
|
|
ae_vector_set_length(&cc->f, tblsize, _state);
|
|
ae_v_move(&cc->x.ptr.p_double[0], 1, &c->x.ptr.p_double[0], 1, ae_v_len(0,cc->n-1));
|
|
ae_v_move(&cc->y.ptr.p_double[0], 1, &c->y.ptr.p_double[0], 1, ae_v_len(0,cc->m-1));
|
|
ae_v_move(&cc->z.ptr.p_double[0], 1, &c->z.ptr.p_double[0], 1, ae_v_len(0,cc->l-1));
|
|
ae_v_move(&cc->f.ptr.p_double[0], 1, &c->f.ptr.p_double[0], 1, ae_v_len(0,tblsize-1));
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Trilinear spline resampling
|
|
|
|
INPUT PARAMETERS:
|
|
A - array[0..OldXCount*OldYCount*OldZCount-1], function
|
|
values at the old grid, :
|
|
A[0] x=0,y=0,z=0
|
|
A[1] x=1,y=0,z=0
|
|
A[..] ...
|
|
A[..] x=oldxcount-1,y=0,z=0
|
|
A[..] x=0,y=1,z=0
|
|
A[..] ...
|
|
...
|
|
OldZCount - old Z-count, OldZCount>1
|
|
OldYCount - old Y-count, OldYCount>1
|
|
OldXCount - old X-count, OldXCount>1
|
|
NewZCount - new Z-count, NewZCount>1
|
|
NewYCount - new Y-count, NewYCount>1
|
|
NewXCount - new X-count, NewXCount>1
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - array[0..NewXCount*NewYCount*NewZCount-1], function
|
|
values at the new grid:
|
|
B[0] x=0,y=0,z=0
|
|
B[1] x=1,y=0,z=0
|
|
B[..] ...
|
|
B[..] x=newxcount-1,y=0,z=0
|
|
B[..] x=0,y=1,z=0
|
|
B[..] ...
|
|
...
|
|
|
|
-- ALGLIB routine --
|
|
26.04.2012
|
|
Copyright by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dresampletrilinear(/* Real */ ae_vector* a,
|
|
ae_int_t oldzcount,
|
|
ae_int_t oldycount,
|
|
ae_int_t oldxcount,
|
|
ae_int_t newzcount,
|
|
ae_int_t newycount,
|
|
ae_int_t newxcount,
|
|
/* Real */ ae_vector* b,
|
|
ae_state *_state)
|
|
{
|
|
double xd;
|
|
double yd;
|
|
double zd;
|
|
double c0;
|
|
double c1;
|
|
double c2;
|
|
double c3;
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t iz;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
|
|
ae_vector_clear(b);
|
|
|
|
ae_assert((oldycount>1&&oldzcount>1)&&oldxcount>1, "Spline3DResampleTrilinear: length/width/height less than 1", _state);
|
|
ae_assert((newycount>1&&newzcount>1)&&newxcount>1, "Spline3DResampleTrilinear: length/width/height less than 1", _state);
|
|
ae_assert(a->cnt>=oldycount*oldzcount*oldxcount, "Spline3DResampleTrilinear: length/width/height less than 1", _state);
|
|
ae_vector_set_length(b, newxcount*newycount*newzcount, _state);
|
|
for(i=0; i<=newxcount-1; i++)
|
|
{
|
|
for(j=0; j<=newycount-1; j++)
|
|
{
|
|
for(k=0; k<=newzcount-1; k++)
|
|
{
|
|
ix = i*(oldxcount-1)/(newxcount-1);
|
|
if( ix==oldxcount-1 )
|
|
{
|
|
ix = oldxcount-2;
|
|
}
|
|
xd = (double)(i*(oldxcount-1))/(double)(newxcount-1)-ix;
|
|
iy = j*(oldycount-1)/(newycount-1);
|
|
if( iy==oldycount-1 )
|
|
{
|
|
iy = oldycount-2;
|
|
}
|
|
yd = (double)(j*(oldycount-1))/(double)(newycount-1)-iy;
|
|
iz = k*(oldzcount-1)/(newzcount-1);
|
|
if( iz==oldzcount-1 )
|
|
{
|
|
iz = oldzcount-2;
|
|
}
|
|
zd = (double)(k*(oldzcount-1))/(double)(newzcount-1)-iz;
|
|
c0 = a->ptr.p_double[oldxcount*(oldycount*iz+iy)+ix]*(1-xd)+a->ptr.p_double[oldxcount*(oldycount*iz+iy)+(ix+1)]*xd;
|
|
c1 = a->ptr.p_double[oldxcount*(oldycount*iz+(iy+1))+ix]*(1-xd)+a->ptr.p_double[oldxcount*(oldycount*iz+(iy+1))+(ix+1)]*xd;
|
|
c2 = a->ptr.p_double[oldxcount*(oldycount*(iz+1)+iy)+ix]*(1-xd)+a->ptr.p_double[oldxcount*(oldycount*(iz+1)+iy)+(ix+1)]*xd;
|
|
c3 = a->ptr.p_double[oldxcount*(oldycount*(iz+1)+(iy+1))+ix]*(1-xd)+a->ptr.p_double[oldxcount*(oldycount*(iz+1)+(iy+1))+(ix+1)]*xd;
|
|
c0 = c0*(1-yd)+c1*yd;
|
|
c1 = c2*(1-yd)+c3*yd;
|
|
b->ptr.p_double[newxcount*(newycount*k+j)+i] = c0*(1-zd)+c1*zd;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine builds trilinear vector-valued spline.
|
|
|
|
INPUT PARAMETERS:
|
|
X - spline abscissas, array[0..N-1]
|
|
Y - spline ordinates, array[0..M-1]
|
|
Z - spline applicates, array[0..L-1]
|
|
F - function values, array[0..M*N*L*D-1]:
|
|
* first D elements store D values at (X[0],Y[0],Z[0])
|
|
* next D elements store D values at (X[1],Y[0],Z[0])
|
|
* next D elements store D values at (X[2],Y[0],Z[0])
|
|
* ...
|
|
* next D elements store D values at (X[0],Y[1],Z[0])
|
|
* next D elements store D values at (X[1],Y[1],Z[0])
|
|
* next D elements store D values at (X[2],Y[1],Z[0])
|
|
* ...
|
|
* next D elements store D values at (X[0],Y[0],Z[1])
|
|
* next D elements store D values at (X[1],Y[0],Z[1])
|
|
* next D elements store D values at (X[2],Y[0],Z[1])
|
|
* ...
|
|
* general form - D function values at (X[i],Y[j]) are stored
|
|
at F[D*(N*(M*K+J)+I)...D*(N*(M*K+J)+I)+D-1].
|
|
M,N,
|
|
L - grid size, M>=2, N>=2, L>=2
|
|
D - vector dimension, D>=1
|
|
|
|
OUTPUT PARAMETERS:
|
|
C - spline interpolant
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dbuildtrilinearv(/* Real */ ae_vector* x,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t m,
|
|
/* Real */ ae_vector* z,
|
|
ae_int_t l,
|
|
/* Real */ ae_vector* f,
|
|
ae_int_t d,
|
|
spline3dinterpolant* c,
|
|
ae_state *_state)
|
|
{
|
|
double t;
|
|
ae_int_t tblsize;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t i0;
|
|
ae_int_t j0;
|
|
|
|
_spline3dinterpolant_clear(c);
|
|
|
|
ae_assert(m>=2, "Spline3DBuildTrilinearV: M<2", _state);
|
|
ae_assert(n>=2, "Spline3DBuildTrilinearV: N<2", _state);
|
|
ae_assert(l>=2, "Spline3DBuildTrilinearV: L<2", _state);
|
|
ae_assert(d>=1, "Spline3DBuildTrilinearV: D<1", _state);
|
|
ae_assert((x->cnt>=n&&y->cnt>=m)&&z->cnt>=l, "Spline3DBuildTrilinearV: length of X, Y or Z is too short (Length(X/Y/Z)<N/M/L)", _state);
|
|
ae_assert((isfinitevector(x, n, _state)&&isfinitevector(y, m, _state))&&isfinitevector(z, l, _state), "Spline3DBuildTrilinearV: X, Y or Z contains NaN or Infinite value", _state);
|
|
tblsize = n*m*l*d;
|
|
ae_assert(f->cnt>=tblsize, "Spline3DBuildTrilinearV: length of F is too short (Length(F)<N*M*L*D)", _state);
|
|
ae_assert(isfinitevector(f, tblsize, _state), "Spline3DBuildTrilinearV: F contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Fill interpolant
|
|
*/
|
|
c->k = 1;
|
|
c->n = n;
|
|
c->m = m;
|
|
c->l = l;
|
|
c->d = d;
|
|
c->stype = -1;
|
|
ae_vector_set_length(&c->x, c->n, _state);
|
|
ae_vector_set_length(&c->y, c->m, _state);
|
|
ae_vector_set_length(&c->z, c->l, _state);
|
|
ae_vector_set_length(&c->f, tblsize, _state);
|
|
for(i=0; i<=c->n-1; i++)
|
|
{
|
|
c->x.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
c->y.ptr.p_double[i] = y->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=c->l-1; i++)
|
|
{
|
|
c->z.ptr.p_double[i] = z->ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=tblsize-1; i++)
|
|
{
|
|
c->f.ptr.p_double[i] = f->ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Sort points:
|
|
* * sort x;
|
|
* * sort y;
|
|
* * sort z.
|
|
*/
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
k = j;
|
|
for(i=j+1; i<=c->n-1; i++)
|
|
{
|
|
if( ae_fp_less(c->x.ptr.p_double[i],c->x.ptr.p_double[k]) )
|
|
{
|
|
k = i;
|
|
}
|
|
}
|
|
if( k!=j )
|
|
{
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
for(j0=0; j0<=c->l-1; j0++)
|
|
{
|
|
for(i0=0; i0<=c->d-1; i0++)
|
|
{
|
|
t = c->f.ptr.p_double[c->d*(c->n*(c->m*j0+i)+j)+i0];
|
|
c->f.ptr.p_double[c->d*(c->n*(c->m*j0+i)+j)+i0] = c->f.ptr.p_double[c->d*(c->n*(c->m*j0+i)+k)+i0];
|
|
c->f.ptr.p_double[c->d*(c->n*(c->m*j0+i)+k)+i0] = t;
|
|
}
|
|
}
|
|
}
|
|
t = c->x.ptr.p_double[j];
|
|
c->x.ptr.p_double[j] = c->x.ptr.p_double[k];
|
|
c->x.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
for(i=0; i<=c->m-1; i++)
|
|
{
|
|
k = i;
|
|
for(j=i+1; j<=c->m-1; j++)
|
|
{
|
|
if( ae_fp_less(c->y.ptr.p_double[j],c->y.ptr.p_double[k]) )
|
|
{
|
|
k = j;
|
|
}
|
|
}
|
|
if( k!=i )
|
|
{
|
|
for(j=0; j<=c->n-1; j++)
|
|
{
|
|
for(j0=0; j0<=c->l-1; j0++)
|
|
{
|
|
for(i0=0; i0<=c->d-1; i0++)
|
|
{
|
|
t = c->f.ptr.p_double[c->d*(c->n*(c->m*j0+i)+j)+i0];
|
|
c->f.ptr.p_double[c->d*(c->n*(c->m*j0+i)+j)+i0] = c->f.ptr.p_double[c->d*(c->n*(c->m*j0+k)+j)+i0];
|
|
c->f.ptr.p_double[c->d*(c->n*(c->m*j0+k)+j)+i0] = t;
|
|
}
|
|
}
|
|
}
|
|
t = c->y.ptr.p_double[i];
|
|
c->y.ptr.p_double[i] = c->y.ptr.p_double[k];
|
|
c->y.ptr.p_double[k] = t;
|
|
}
|
|
}
|
|
for(k=0; k<=c->l-1; k++)
|
|
{
|
|
i = k;
|
|
for(j=i+1; j<=c->l-1; j++)
|
|
{
|
|
if( ae_fp_less(c->z.ptr.p_double[j],c->z.ptr.p_double[i]) )
|
|
{
|
|
i = j;
|
|
}
|
|
}
|
|
if( i!=k )
|
|
{
|
|
for(j=0; j<=c->m-1; j++)
|
|
{
|
|
for(j0=0; j0<=c->n-1; j0++)
|
|
{
|
|
for(i0=0; i0<=c->d-1; i0++)
|
|
{
|
|
t = c->f.ptr.p_double[c->d*(c->n*(c->m*k+j)+j0)+i0];
|
|
c->f.ptr.p_double[c->d*(c->n*(c->m*k+j)+j0)+i0] = c->f.ptr.p_double[c->d*(c->n*(c->m*i+j)+j0)+i0];
|
|
c->f.ptr.p_double[c->d*(c->n*(c->m*i+j)+j0)+i0] = t;
|
|
}
|
|
}
|
|
}
|
|
t = c->z.ptr.p_double[k];
|
|
c->z.ptr.p_double[k] = c->z.ptr.p_double[i];
|
|
c->z.ptr.p_double[i] = t;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y,Z).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y,
|
|
Z - point
|
|
F - output buffer, possibly preallocated array. In case array size
|
|
is large enough to store result, it is not reallocated. Array
|
|
which is too short will be reallocated
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] (or larger) which stores function values
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dcalcvbuf(spline3dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
double z,
|
|
/* Real */ ae_vector* f,
|
|
ae_state *_state)
|
|
{
|
|
double xd;
|
|
double yd;
|
|
double zd;
|
|
double c0;
|
|
double c1;
|
|
double c2;
|
|
double c3;
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t iz;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t h;
|
|
ae_int_t i;
|
|
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline3DCalcVBuf: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert((ae_isfinite(x, _state)&&ae_isfinite(y, _state))&&ae_isfinite(z, _state), "Spline3DCalcVBuf: X, Y or Z contains NaN/Infinite", _state);
|
|
rvectorsetlengthatleast(f, c->d, _state);
|
|
|
|
/*
|
|
* Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->n-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->x.ptr.p_double[h],x) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
ix = l;
|
|
|
|
/*
|
|
* Binary search in the [ y[0], ..., y[n-2] ] (y[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->m-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->y.ptr.p_double[h],y) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
iy = l;
|
|
|
|
/*
|
|
* Binary search in the [ z[0], ..., z[n-2] ] (z[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->l-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->z.ptr.p_double[h],z) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
iz = l;
|
|
xd = (x-c->x.ptr.p_double[ix])/(c->x.ptr.p_double[ix+1]-c->x.ptr.p_double[ix]);
|
|
yd = (y-c->y.ptr.p_double[iy])/(c->y.ptr.p_double[iy+1]-c->y.ptr.p_double[iy]);
|
|
zd = (z-c->z.ptr.p_double[iz])/(c->z.ptr.p_double[iz+1]-c->z.ptr.p_double[iz]);
|
|
for(i=0; i<=c->d-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Trilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
c0 = c->f.ptr.p_double[c->d*(c->n*(c->m*iz+iy)+ix)+i]*(1-xd)+c->f.ptr.p_double[c->d*(c->n*(c->m*iz+iy)+(ix+1))+i]*xd;
|
|
c1 = c->f.ptr.p_double[c->d*(c->n*(c->m*iz+(iy+1))+ix)+i]*(1-xd)+c->f.ptr.p_double[c->d*(c->n*(c->m*iz+(iy+1))+(ix+1))+i]*xd;
|
|
c2 = c->f.ptr.p_double[c->d*(c->n*(c->m*(iz+1)+iy)+ix)+i]*(1-xd)+c->f.ptr.p_double[c->d*(c->n*(c->m*(iz+1)+iy)+(ix+1))+i]*xd;
|
|
c3 = c->f.ptr.p_double[c->d*(c->n*(c->m*(iz+1)+(iy+1))+ix)+i]*(1-xd)+c->f.ptr.p_double[c->d*(c->n*(c->m*(iz+1)+(iy+1))+(ix+1))+i]*xd;
|
|
c0 = c0*(1-yd)+c1*yd;
|
|
c1 = c2*(1-yd)+c3*yd;
|
|
f->ptr.p_double[i] = c0*(1-zd)+c1*zd;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates trilinear or tricubic vector-valued spline at the
|
|
given point (X,Y,Z).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y,
|
|
Z - point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - array[D] which stores function values. F is out-parameter and
|
|
it is reallocated after call to this function. In case you
|
|
want to reuse previously allocated F, you may use
|
|
Spline2DCalcVBuf(), which reallocates F only when it is too
|
|
small.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dcalcv(spline3dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
double z,
|
|
/* Real */ ae_vector* f,
|
|
ae_state *_state)
|
|
{
|
|
|
|
ae_vector_clear(f);
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline3DCalcV: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert((ae_isfinite(x, _state)&&ae_isfinite(y, _state))&&ae_isfinite(z, _state), "Spline3DCalcV: X=NaN/Infinite, Y=NaN/Infinite or Z=NaN/Infinite", _state);
|
|
ae_vector_set_length(f, c->d, _state);
|
|
spline3dcalcvbuf(c, x, y, z, f, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine unpacks tri-dimensional spline into the coefficients table
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
|
|
Result:
|
|
N - grid size (X)
|
|
M - grid size (Y)
|
|
L - grid size (Z)
|
|
D - number of components
|
|
SType- spline type. Currently, only one spline type is supported:
|
|
trilinear spline, as indicated by SType=1.
|
|
Tbl - spline coefficients: [0..(N-1)*(M-1)*(L-1)*D-1, 0..13].
|
|
For T=0..D-1 (component index), I = 0...N-2 (x index),
|
|
J=0..M-2 (y index), K=0..L-2 (z index):
|
|
Q := T + I*D + J*D*(N-1) + K*D*(N-1)*(M-1),
|
|
|
|
Q-th row stores decomposition for T-th component of the
|
|
vector-valued function
|
|
|
|
Tbl[Q,0] = X[i]
|
|
Tbl[Q,1] = X[i+1]
|
|
Tbl[Q,2] = Y[j]
|
|
Tbl[Q,3] = Y[j+1]
|
|
Tbl[Q,4] = Z[k]
|
|
Tbl[Q,5] = Z[k+1]
|
|
|
|
Tbl[Q,6] = C000
|
|
Tbl[Q,7] = C100
|
|
Tbl[Q,8] = C010
|
|
Tbl[Q,9] = C110
|
|
Tbl[Q,10]= C001
|
|
Tbl[Q,11]= C101
|
|
Tbl[Q,12]= C011
|
|
Tbl[Q,13]= C111
|
|
On each grid square spline is equals to:
|
|
S(x) = SUM(c[i,j,k]*(x^i)*(y^j)*(z^k), i=0..1, j=0..1, k=0..1)
|
|
t = x-x[j]
|
|
u = y-y[i]
|
|
v = z-z[k]
|
|
|
|
NOTE: format of Tbl is given for SType=1. Future versions of
|
|
ALGLIB can use different formats for different values of
|
|
SType.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline3dunpackv(spline3dinterpolant* c,
|
|
ae_int_t* n,
|
|
ae_int_t* m,
|
|
ae_int_t* l,
|
|
ae_int_t* d,
|
|
ae_int_t* stype,
|
|
/* Real */ ae_matrix* tbl,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t p;
|
|
ae_int_t ci;
|
|
ae_int_t cj;
|
|
ae_int_t ck;
|
|
double du;
|
|
double dv;
|
|
double dw;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t di;
|
|
ae_int_t i0;
|
|
|
|
*n = 0;
|
|
*m = 0;
|
|
*l = 0;
|
|
*d = 0;
|
|
*stype = 0;
|
|
ae_matrix_clear(tbl);
|
|
|
|
ae_assert(c->stype==-1, "Spline3DUnpackV: incorrect C (incorrect parameter C.SType)", _state);
|
|
*n = c->n;
|
|
*m = c->m;
|
|
*l = c->l;
|
|
*d = c->d;
|
|
*stype = ae_iabs(c->stype, _state);
|
|
ae_matrix_set_length(tbl, (*n-1)*(*m-1)*(*l-1)*(*d), 14, _state);
|
|
|
|
/*
|
|
* Fill
|
|
*/
|
|
for(i=0; i<=*n-2; i++)
|
|
{
|
|
for(j=0; j<=*m-2; j++)
|
|
{
|
|
for(k=0; k<=*l-2; k++)
|
|
{
|
|
for(di=0; di<=*d-1; di++)
|
|
{
|
|
p = *d*((*n-1)*((*m-1)*k+j)+i)+di;
|
|
tbl->ptr.pp_double[p][0] = c->x.ptr.p_double[i];
|
|
tbl->ptr.pp_double[p][1] = c->x.ptr.p_double[i+1];
|
|
tbl->ptr.pp_double[p][2] = c->y.ptr.p_double[j];
|
|
tbl->ptr.pp_double[p][3] = c->y.ptr.p_double[j+1];
|
|
tbl->ptr.pp_double[p][4] = c->z.ptr.p_double[k];
|
|
tbl->ptr.pp_double[p][5] = c->z.ptr.p_double[k+1];
|
|
du = 1/(tbl->ptr.pp_double[p][1]-tbl->ptr.pp_double[p][0]);
|
|
dv = 1/(tbl->ptr.pp_double[p][3]-tbl->ptr.pp_double[p][2]);
|
|
dw = 1/(tbl->ptr.pp_double[p][5]-tbl->ptr.pp_double[p][4]);
|
|
|
|
/*
|
|
* Trilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
for(i0=6; i0<=13; i0++)
|
|
{
|
|
tbl->ptr.pp_double[p][i0] = 0;
|
|
}
|
|
tbl->ptr.pp_double[p][6+2*(2*0+0)+0] = c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
tbl->ptr.pp_double[p][6+2*(2*0+0)+1] = c->f.ptr.p_double[*d*(*n*(*m*k+j)+(i+1))+di]-c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
tbl->ptr.pp_double[p][6+2*(2*0+1)+0] = c->f.ptr.p_double[*d*(*n*(*m*k+(j+1))+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
tbl->ptr.pp_double[p][6+2*(2*0+1)+1] = c->f.ptr.p_double[*d*(*n*(*m*k+(j+1))+(i+1))+di]-c->f.ptr.p_double[*d*(*n*(*m*k+(j+1))+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*k+j)+(i+1))+di]+c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
tbl->ptr.pp_double[p][6+2*(2*1+0)+0] = c->f.ptr.p_double[*d*(*n*(*m*(k+1)+j)+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
tbl->ptr.pp_double[p][6+2*(2*1+0)+1] = c->f.ptr.p_double[*d*(*n*(*m*(k+1)+j)+(i+1))+di]-c->f.ptr.p_double[*d*(*n*(*m*(k+1)+j)+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*k+j)+(i+1))+di]+c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
tbl->ptr.pp_double[p][6+2*(2*1+1)+0] = c->f.ptr.p_double[*d*(*n*(*m*(k+1)+(j+1))+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*(k+1)+j)+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*k+(j+1))+i)+di]+c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
tbl->ptr.pp_double[p][6+2*(2*1+1)+1] = c->f.ptr.p_double[*d*(*n*(*m*(k+1)+(j+1))+(i+1))+di]-c->f.ptr.p_double[*d*(*n*(*m*(k+1)+(j+1))+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*(k+1)+j)+(i+1))+di]+c->f.ptr.p_double[*d*(*n*(*m*(k+1)+j)+i)+di]-c->f.ptr.p_double[*d*(*n*(*m*k+(j+1))+(i+1))+di]+c->f.ptr.p_double[*d*(*n*(*m*k+(j+1))+i)+di]+c->f.ptr.p_double[*d*(*n*(*m*k+j)+(i+1))+di]-c->f.ptr.p_double[*d*(*n*(*m*k+j)+i)+di];
|
|
}
|
|
|
|
/*
|
|
* Rescale Cij
|
|
*/
|
|
for(ci=0; ci<=1; ci++)
|
|
{
|
|
for(cj=0; cj<=1; cj++)
|
|
{
|
|
for(ck=0; ck<=1; ck++)
|
|
{
|
|
tbl->ptr.pp_double[p][6+2*(2*ck+cj)+ci] = tbl->ptr.pp_double[p][6+2*(2*ck+cj)+ci]*ae_pow(du, ci, _state)*ae_pow(dv, cj, _state)*ae_pow(dw, ck, _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the trilinear(or tricubic;possible
|
|
will be later) spline at the given point X(and its derivatives; possible
|
|
will be later).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y, Z - point
|
|
|
|
OUTPUT PARAMETERS:
|
|
F - S(x,y,z)
|
|
FX - dS(x,y,z)/dX
|
|
FY - dS(x,y,z)/dY
|
|
FXY - d2S(x,y,z)/dXdY
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 26.04.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline3d_spline3ddiff(spline3dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
double z,
|
|
double* f,
|
|
double* fx,
|
|
double* fy,
|
|
double* fxy,
|
|
ae_state *_state)
|
|
{
|
|
double xd;
|
|
double yd;
|
|
double zd;
|
|
double c0;
|
|
double c1;
|
|
double c2;
|
|
double c3;
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t iz;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t h;
|
|
|
|
*f = 0;
|
|
*fx = 0;
|
|
*fy = 0;
|
|
*fxy = 0;
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline3DDiff: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(x, _state)&&ae_isfinite(y, _state), "Spline3DDiff: X or Y contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Prepare F, dF/dX, dF/dY, d2F/dXdY
|
|
*/
|
|
*f = 0;
|
|
*fx = 0;
|
|
*fy = 0;
|
|
*fxy = 0;
|
|
if( c->d!=1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Binary search in the [ x[0], ..., x[n-2] ] (x[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->n-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->x.ptr.p_double[h],x) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
ix = l;
|
|
|
|
/*
|
|
* Binary search in the [ y[0], ..., y[n-2] ] (y[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->m-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->y.ptr.p_double[h],y) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
iy = l;
|
|
|
|
/*
|
|
* Binary search in the [ z[0], ..., z[n-2] ] (z[n-1] is not included)
|
|
*/
|
|
l = 0;
|
|
r = c->l-1;
|
|
while(l!=r-1)
|
|
{
|
|
h = (l+r)/2;
|
|
if( ae_fp_greater_eq(c->z.ptr.p_double[h],z) )
|
|
{
|
|
r = h;
|
|
}
|
|
else
|
|
{
|
|
l = h;
|
|
}
|
|
}
|
|
iz = l;
|
|
xd = (x-c->x.ptr.p_double[ix])/(c->x.ptr.p_double[ix+1]-c->x.ptr.p_double[ix]);
|
|
yd = (y-c->y.ptr.p_double[iy])/(c->y.ptr.p_double[iy+1]-c->y.ptr.p_double[iy]);
|
|
zd = (z-c->z.ptr.p_double[iz])/(c->z.ptr.p_double[iz+1]-c->z.ptr.p_double[iz]);
|
|
|
|
/*
|
|
* Trilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
c0 = c->f.ptr.p_double[c->n*(c->m*iz+iy)+ix]*(1-xd)+c->f.ptr.p_double[c->n*(c->m*iz+iy)+(ix+1)]*xd;
|
|
c1 = c->f.ptr.p_double[c->n*(c->m*iz+(iy+1))+ix]*(1-xd)+c->f.ptr.p_double[c->n*(c->m*iz+(iy+1))+(ix+1)]*xd;
|
|
c2 = c->f.ptr.p_double[c->n*(c->m*(iz+1)+iy)+ix]*(1-xd)+c->f.ptr.p_double[c->n*(c->m*(iz+1)+iy)+(ix+1)]*xd;
|
|
c3 = c->f.ptr.p_double[c->n*(c->m*(iz+1)+(iy+1))+ix]*(1-xd)+c->f.ptr.p_double[c->n*(c->m*(iz+1)+(iy+1))+(ix+1)]*xd;
|
|
c0 = c0*(1-yd)+c1*yd;
|
|
c1 = c2*(1-yd)+c3*yd;
|
|
*f = c0*(1-zd)+c1*zd;
|
|
}
|
|
}
|
|
|
|
|
|
ae_bool _spline3dinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline3dinterpolant *p = (spline3dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
if( !ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->z, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init(&p->f, 0, DT_REAL, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
ae_bool _spline3dinterpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline3dinterpolant *dst = (spline3dinterpolant*)_dst;
|
|
spline3dinterpolant *src = (spline3dinterpolant*)_src;
|
|
dst->k = src->k;
|
|
dst->stype = src->stype;
|
|
dst->n = src->n;
|
|
dst->m = src->m;
|
|
dst->l = src->l;
|
|
dst->d = src->d;
|
|
if( !ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->z, &src->z, _state, make_automatic) )
|
|
return ae_false;
|
|
if( !ae_vector_init_copy(&dst->f, &src->f, _state, make_automatic) )
|
|
return ae_false;
|
|
return ae_true;
|
|
}
|
|
|
|
|
|
void _spline3dinterpolant_clear(void* _p)
|
|
{
|
|
spline3dinterpolant *p = (spline3dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->x);
|
|
ae_vector_clear(&p->y);
|
|
ae_vector_clear(&p->z);
|
|
ae_vector_clear(&p->f);
|
|
}
|
|
|
|
|
|
void _spline3dinterpolant_destroy(void* _p)
|
|
{
|
|
spline3dinterpolant *p = (spline3dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->x);
|
|
ae_vector_destroy(&p->y);
|
|
ae_vector_destroy(&p->z);
|
|
ae_vector_destroy(&p->f);
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|