55961 lines
2.0 MiB
Executable File
55961 lines
2.0 MiB
Executable File
/*************************************************************************
|
|
ALGLIB 3.16.0 (source code generated 2019-12-19)
|
|
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 >>>
|
|
*************************************************************************/
|
|
#ifdef _MSC_VER
|
|
#define _CRT_SECURE_NO_WARNINGS
|
|
#endif
|
|
#include "stdafx.h"
|
|
#include "interpolation.h"
|
|
|
|
// disable some irrelevant warnings
|
|
#if (AE_COMPILER==AE_MSVC) && !defined(AE_ALL_WARNINGS)
|
|
#pragma warning(disable:4100)
|
|
#pragma warning(disable:4127)
|
|
#pragma warning(disable:4611)
|
|
#pragma warning(disable:4702)
|
|
#pragma warning(disable:4996)
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// THIS SECTION CONTAINS IMPLEMENTATION OF C++ INTERFACE
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
namespace alglib
|
|
{
|
|
|
|
#if defined(AE_COMPILE_IDW) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RATINT) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_FITSPHERE) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_INTFITSERV) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_SPLINE1D) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_PARAMETRIC) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_SPLINE3D) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_POLINT) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_LSFIT) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RBFV2) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_SPLINE2D) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RBFV1) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RBF) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_INTCOMP) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_IDW) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
Buffer object which is used to perform evaluation requests in the
|
|
multithreaded mode (multiple threads working with same IDW object).
|
|
|
|
This object should be created with idwcreatecalcbuffer().
|
|
*************************************************************************/
|
|
_idwcalcbuffer_owner::_idwcalcbuffer_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwcalcbuffer_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::idwcalcbuffer*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwcalcbuffer), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwcalcbuffer));
|
|
alglib_impl::_idwcalcbuffer_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwcalcbuffer_owner::_idwcalcbuffer_owner(const _idwcalcbuffer_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwcalcbuffer_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwcalcbuffer copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::idwcalcbuffer*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwcalcbuffer), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwcalcbuffer));
|
|
alglib_impl::_idwcalcbuffer_init_copy(p_struct, const_cast<alglib_impl::idwcalcbuffer*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwcalcbuffer_owner& _idwcalcbuffer_owner::operator=(const _idwcalcbuffer_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: idwcalcbuffer assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwcalcbuffer assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_idwcalcbuffer_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwcalcbuffer));
|
|
alglib_impl::_idwcalcbuffer_init_copy(p_struct, const_cast<alglib_impl::idwcalcbuffer*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_idwcalcbuffer_owner::~_idwcalcbuffer_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwcalcbuffer_destroy(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
}
|
|
|
|
alglib_impl::idwcalcbuffer* _idwcalcbuffer_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::idwcalcbuffer* _idwcalcbuffer_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::idwcalcbuffer*>(p_struct);
|
|
}
|
|
idwcalcbuffer::idwcalcbuffer() : _idwcalcbuffer_owner()
|
|
{
|
|
}
|
|
|
|
idwcalcbuffer::idwcalcbuffer(const idwcalcbuffer &rhs):_idwcalcbuffer_owner(rhs)
|
|
{
|
|
}
|
|
|
|
idwcalcbuffer& idwcalcbuffer::operator=(const idwcalcbuffer &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_idwcalcbuffer_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
idwcalcbuffer::~idwcalcbuffer()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW (Inverse Distance Weighting) model object.
|
|
*************************************************************************/
|
|
_idwmodel_owner::_idwmodel_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwmodel_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::idwmodel*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwmodel), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwmodel));
|
|
alglib_impl::_idwmodel_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwmodel_owner::_idwmodel_owner(const _idwmodel_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwmodel_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwmodel copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::idwmodel*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwmodel), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwmodel));
|
|
alglib_impl::_idwmodel_init_copy(p_struct, const_cast<alglib_impl::idwmodel*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwmodel_owner& _idwmodel_owner::operator=(const _idwmodel_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: idwmodel assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwmodel assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_idwmodel_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwmodel));
|
|
alglib_impl::_idwmodel_init_copy(p_struct, const_cast<alglib_impl::idwmodel*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_idwmodel_owner::~_idwmodel_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwmodel_destroy(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
}
|
|
|
|
alglib_impl::idwmodel* _idwmodel_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::idwmodel* _idwmodel_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::idwmodel*>(p_struct);
|
|
}
|
|
idwmodel::idwmodel() : _idwmodel_owner()
|
|
{
|
|
}
|
|
|
|
idwmodel::idwmodel(const idwmodel &rhs):_idwmodel_owner(rhs)
|
|
{
|
|
}
|
|
|
|
idwmodel& idwmodel::operator=(const idwmodel &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_idwmodel_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
idwmodel::~idwmodel()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Builder object used to generate IDW (Inverse Distance Weighting) model.
|
|
*************************************************************************/
|
|
_idwbuilder_owner::_idwbuilder_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwbuilder_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::idwbuilder*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwbuilder), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwbuilder));
|
|
alglib_impl::_idwbuilder_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwbuilder_owner::_idwbuilder_owner(const _idwbuilder_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwbuilder_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwbuilder copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::idwbuilder*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwbuilder), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwbuilder));
|
|
alglib_impl::_idwbuilder_init_copy(p_struct, const_cast<alglib_impl::idwbuilder*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwbuilder_owner& _idwbuilder_owner::operator=(const _idwbuilder_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: idwbuilder assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwbuilder assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_idwbuilder_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwbuilder));
|
|
alglib_impl::_idwbuilder_init_copy(p_struct, const_cast<alglib_impl::idwbuilder*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_idwbuilder_owner::~_idwbuilder_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwbuilder_destroy(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
}
|
|
|
|
alglib_impl::idwbuilder* _idwbuilder_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::idwbuilder* _idwbuilder_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::idwbuilder*>(p_struct);
|
|
}
|
|
idwbuilder::idwbuilder() : _idwbuilder_owner()
|
|
{
|
|
}
|
|
|
|
idwbuilder::idwbuilder(const idwbuilder &rhs):_idwbuilder_owner(rhs)
|
|
{
|
|
}
|
|
|
|
idwbuilder& idwbuilder::operator=(const idwbuilder &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_idwbuilder_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
idwbuilder::~idwbuilder()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW fitting report:
|
|
rmserror RMS error
|
|
avgerror average error
|
|
maxerror maximum error
|
|
r2 coefficient of determination, R-squared, 1-RSS/TSS
|
|
*************************************************************************/
|
|
_idwreport_owner::_idwreport_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::idwreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwreport));
|
|
alglib_impl::_idwreport_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwreport_owner::_idwreport_owner(const _idwreport_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwreport copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::idwreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::idwreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwreport));
|
|
alglib_impl::_idwreport_init_copy(p_struct, const_cast<alglib_impl::idwreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_idwreport_owner& _idwreport_owner::operator=(const _idwreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: idwreport assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: idwreport assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_idwreport_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::idwreport));
|
|
alglib_impl::_idwreport_init_copy(p_struct, const_cast<alglib_impl::idwreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_idwreport_owner::~_idwreport_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_idwreport_destroy(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
}
|
|
|
|
alglib_impl::idwreport* _idwreport_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::idwreport* _idwreport_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::idwreport*>(p_struct);
|
|
}
|
|
idwreport::idwreport() : _idwreport_owner() ,rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),maxerror(p_struct->maxerror),r2(p_struct->r2)
|
|
{
|
|
}
|
|
|
|
idwreport::idwreport(const idwreport &rhs):_idwreport_owner(rhs) ,rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),maxerror(p_struct->maxerror),r2(p_struct->r2)
|
|
{
|
|
}
|
|
|
|
idwreport& idwreport::operator=(const idwreport &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_idwreport_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
idwreport::~idwreport()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 idwserialize(idwmodel &obj, std::string &s_out)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
alglib_impl::ae_int_t ssize;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_alloc_start(&serializer);
|
|
alglib_impl::idwalloc(&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::idwserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_assert( s_out.length()<=(size_t)ssize, "ALGLIB: serialization integrity error", &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
/*************************************************************************
|
|
This function unserializes data structure from string.
|
|
*************************************************************************/
|
|
void idwunserialize(const std::string &s_in, idwmodel &obj)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_ustart_str(&serializer, &s_in);
|
|
alglib_impl::idwunserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function serializes data structure to C++ stream.
|
|
|
|
Data stream generated by this function is same as string representation
|
|
generated by string version of serializer - alphanumeric characters,
|
|
dots, underscores, minus signs, which are grouped into words separated by
|
|
spaces and CR+LF.
|
|
|
|
We recommend you to read comments on string version of serializer to find
|
|
out more about serialization of AlGLIB objects.
|
|
*************************************************************************/
|
|
void idwserialize(idwmodel &obj, std::ostream &s_out)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_alloc_start(&serializer);
|
|
alglib_impl::idwalloc(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_get_alloc_size(&serializer); // not actually needed, but we have to ask
|
|
alglib_impl::ae_serializer_sstart_stream(&serializer, &s_out);
|
|
alglib_impl::idwserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
/*************************************************************************
|
|
This function unserializes data structure from stream.
|
|
*************************************************************************/
|
|
void idwunserialize(const std::istream &s_in, idwmodel &obj)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_ustart_stream(&serializer, &s_in);
|
|
alglib_impl::idwunserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function creates buffer structure which can be used to perform
|
|
parallel IDW model evaluations (with one IDW model instance being
|
|
used from multiple threads, as long as different threads use different
|
|
instances of buffer).
|
|
|
|
This buffer object can be used with idwtscalcbuf() function (here "ts"
|
|
stands for "thread-safe", "buf" is a suffix which denotes function which
|
|
reuses previously allocated output space).
|
|
|
|
How to use it:
|
|
* create IDW model structure or load it from file
|
|
* call idwcreatecalcbuffer(), once per thread working with IDW model (you
|
|
should call this function only AFTER model initialization, see below for
|
|
more information)
|
|
* call idwtscalcbuf() from different threads, with each thread working
|
|
with its own copy of buffer object.
|
|
|
|
INPUT PARAMETERS
|
|
S - IDW model
|
|
|
|
OUTPUT PARAMETERS
|
|
Buf - external buffer.
|
|
|
|
|
|
IMPORTANT: buffer object should be used only with IDW model object which
|
|
was used to initialize buffer. Any attempt to use buffer with
|
|
different object is dangerous - you may get memory violation
|
|
error because sizes of internal arrays do not fit to dimensions
|
|
of the IDW structure.
|
|
|
|
IMPORTANT: you should call this function only for model which was built
|
|
with model builder (or unserialized from file). Sizes of some
|
|
internal structures are determined only after model is built,
|
|
so buffer object created before model construction stage will
|
|
be useless (and any attempt to use it will result in exception).
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
void idwcreatecalcbuffer(const idwmodel &s, idwcalcbuffer &buf, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwcreatecalcbuffer(const_cast<alglib_impl::idwmodel*>(s.c_ptr()), const_cast<alglib_impl::idwcalcbuffer*>(buf.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine creates builder object used to generate IDW model from
|
|
irregularly sampled (scattered) dataset. Multidimensional scalar/vector-
|
|
-valued are supported.
|
|
|
|
Builder object is used to fit model to data as follows:
|
|
* builder object is created with idwbuildercreate() function
|
|
* dataset is added with idwbuildersetpoints() function
|
|
* one of the modern IDW algorithms is chosen with either:
|
|
* idwbuildersetalgomstab() - Multilayer STABilized algorithm (interpolation)
|
|
Alternatively, one of the textbook algorithms can be chosen (not recommended):
|
|
* idwbuildersetalgotextbookshepard() - textbook Shepard algorithm
|
|
* idwbuildersetalgotextbookmodshepard()-textbook modified Shepard algorithm
|
|
* finally, model construction is performed with idwfit() function.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
NX - dimensionality of the argument, NX>=1
|
|
NY - dimensionality of the function being modeled, NY>=1;
|
|
NY=1 corresponds to classic scalar function, NY>=1 corresponds
|
|
to vector-valued function.
|
|
|
|
OUTPUT PARAMETERS:
|
|
State- builder object
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildercreate(const ae_int_t nx, const ae_int_t ny, idwbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildercreate(nx, ny, const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function changes number of layers used by IDW-MSTAB algorithm.
|
|
|
|
The more layers you have, the finer details can be reproduced with IDW
|
|
model. The less layers you have, the less memory and CPU time is consumed
|
|
by the model.
|
|
|
|
Memory consumption grows linearly with layers count, running time grows
|
|
sub-linearly.
|
|
|
|
The default number of layers is 16, which allows you to reproduce details
|
|
at distance down to SRad/65536. You will rarely need to change it.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
NLayers - NLayers>=1, the number of layers used by the model.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetnlayers(const idwbuilder &state, const ae_int_t nlayers, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetnlayers(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), nlayers, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function adds dataset to the builder object.
|
|
|
|
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:
|
|
State - builder object
|
|
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
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset, N>=0.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetpoints(const idwbuilder &state, const real_2d_array &xy, const ae_int_t n, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetpoints(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function adds dataset to the builder object.
|
|
|
|
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:
|
|
State - builder object
|
|
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
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset, N>=0.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void idwbuildersetpoints(const idwbuilder &state, const real_2d_array &xy, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = xy.rows();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetpoints(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
This function sets IDW model construction algorithm to the Multilayer
|
|
Stabilized IDW method (IDW-MSTAB), a latest incarnation of the inverse
|
|
distance weighting interpolation which fixes shortcomings of the original
|
|
and modified Shepard's variants.
|
|
|
|
The distinctive features of IDW-MSTAB are:
|
|
1) exact interpolation is pursued (as opposed to fitting and noise
|
|
suppression)
|
|
2) improved robustness when compared with that of other algorithms:
|
|
* MSTAB shows almost no strange fitting artifacts like ripples and
|
|
sharp spikes (unlike N-dimensional splines and HRBFs)
|
|
* MSTAB does not return function values far from the interval spanned
|
|
by the dataset; say, if all your points have |f|<=1, you can be sure
|
|
that model value won't deviate too much from [-1,+1]
|
|
3) good model construction time competing with that of HRBFs and bicubic
|
|
splines
|
|
4) ability to work with any number of dimensions, starting from NX=1
|
|
|
|
The drawbacks of IDW-MSTAB (and all IDW algorithms in general) are:
|
|
1) dependence of the model evaluation time on the search radius
|
|
2) bad extrapolation properties, models built by this method are usually
|
|
conservative in their predictions
|
|
|
|
Thus, IDW-MSTAB is a good "default" option if you want to perform
|
|
scattered multidimensional interpolation. Although it has its drawbacks,
|
|
it is easy to use and robust, which makes it a good first step.
|
|
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
SRad - initial search radius, SRad>0 is required. A model value
|
|
is obtained by "smart" averaging of the dataset points
|
|
within search radius.
|
|
|
|
NOTE 1: IDW interpolation can correctly handle ANY dataset, including
|
|
datasets with non-distinct points. In case non-distinct points are
|
|
found, an average value for this point will be calculated.
|
|
|
|
NOTE 2: the memory requirements for model storage are O(NPoints*NLayers).
|
|
The model construction needs twice as much memory as model storage.
|
|
|
|
NOTE 3: by default 16 IDW layers are built which is enough for most cases.
|
|
You can change this parameter with idwbuildersetnlayers() method.
|
|
Larger values may be necessary if you need to reproduce extrafine
|
|
details at distances smaller than SRad/65536. Smaller value may
|
|
be necessary if you have to save memory and computing time, and
|
|
ready to sacrifice some model quality.
|
|
|
|
|
|
ALGORITHM DESCRIPTION
|
|
|
|
ALGLIB implementation of IDW is somewhat similar to the modified Shepard's
|
|
method (one with search radius R) but overcomes several of its drawbacks,
|
|
namely:
|
|
1) a tendency to show stepwise behavior for uniform datasets
|
|
2) a tendency to show terrible interpolation properties for highly
|
|
nonuniform datasets which often arise in geospatial tasks
|
|
(function values are densely sampled across multiple separated
|
|
"tracks")
|
|
|
|
IDW-MSTAB method performs several passes over dataset and builds a sequence
|
|
of progressively refined IDW models (layers), which starts from one with
|
|
largest search radius SRad and continues to smaller search radii until
|
|
required number of layers is built. Highest layers reproduce global
|
|
behavior of the target function at larger distances whilst lower layers
|
|
reproduce fine details at smaller distances.
|
|
|
|
Each layer is an IDW model built with following modifications:
|
|
* weights go to zero when distance approach to the current search radius
|
|
* an additional regularizing term is added to the distance: w=1/(d^2+lambda)
|
|
* an additional fictional term with unit weight and zero function value is
|
|
added in order to promote continuity properties at the isolated and
|
|
boundary points
|
|
|
|
By default, 16 layers is built, which is enough for most cases. You can
|
|
change this parameter with idwbuildersetnlayers() method.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetalgomstab(const idwbuilder &state, const double srad, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetalgomstab(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), srad, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets IDW model construction algorithm to the textbook
|
|
Shepard's algorithm with custom (user-specified) power parameter.
|
|
|
|
IMPORTANT: we do NOT recommend using textbook IDW algorithms because they
|
|
have terrible interpolation properties. Use MSTAB in all cases.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
P - power parameter, P>0; good value to start with is 2.0
|
|
|
|
NOTE 1: IDW interpolation can correctly handle ANY dataset, including
|
|
datasets with non-distinct points. In case non-distinct points are
|
|
found, an average value for this point will be calculated.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetalgotextbookshepard(const idwbuilder &state, const double p, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetalgotextbookshepard(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), p, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets IDW model construction algorithm to the 'textbook'
|
|
modified Shepard's algorithm with user-specified search radius.
|
|
|
|
IMPORTANT: we do NOT recommend using textbook IDW algorithms because they
|
|
have terrible interpolation properties. Use MSTAB in all cases.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
R - search radius
|
|
|
|
NOTE 1: IDW interpolation can correctly handle ANY dataset, including
|
|
datasets with non-distinct points. In case non-distinct points are
|
|
found, an average value for this point will be calculated.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetalgotextbookmodshepard(const idwbuilder &state, const double r, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetalgotextbookmodshepard(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), r, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets prior term (model value at infinity) as user-specified
|
|
value.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
V - value for user-defined prior
|
|
|
|
NOTE: for vector-valued models all components of the prior are set to same
|
|
user-specified value
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetuserterm(const idwbuilder &state, const double v, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetuserterm(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), v, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets constant prior term (model value at infinity).
|
|
|
|
Constant prior term is determined as mean value over dataset.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetconstterm(const idwbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetconstterm(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets zero prior term (model value at infinity).
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetzeroterm(const idwbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwbuildersetzeroterm(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
IDW interpolation: scalar target, 1-dimensional argument
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW interpolant built with IDW builder
|
|
X0 - argument value
|
|
|
|
Result:
|
|
IDW interpolant S(X0)
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc1(const idwmodel &s, const double x0, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::idwcalc1(const_cast<alglib_impl::idwmodel*>(s.c_ptr()), x0, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
IDW interpolation: scalar target, 2-dimensional argument
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW interpolant built with IDW builder
|
|
X0, X1 - argument value
|
|
|
|
Result:
|
|
IDW interpolant S(X0,X1)
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc2(const idwmodel &s, const double x0, const double x1, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::idwcalc2(const_cast<alglib_impl::idwmodel*>(s.c_ptr()), x0, x1, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
IDW interpolation: scalar target, 3-dimensional argument
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW interpolant built with IDW builder
|
|
X0,X1,X2- argument value
|
|
|
|
Result:
|
|
IDW interpolant S(X0,X1,X2)
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc3(const idwmodel &s, const double x0, const double x1, const double x2, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::idwcalc3(const_cast<alglib_impl::idwmodel*>(s.c_ptr()), x0, x1, x2, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the IDW 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 idwcalc1(),
|
|
idwcalc2() or idwcalc3().
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW 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 will be
|
|
reallocated after call to this function. In case you want
|
|
to reuse previously allocated Y, you may use idwcalcbuf(),
|
|
which reallocates Y only when it is too small.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwcalc(const idwmodel &s, const real_1d_array &x, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwcalc(const_cast<alglib_impl::idwmodel*>(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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the IDW model at the given point.
|
|
|
|
Same as idwcalc(), but does not reallocate Y when in is large enough to
|
|
store function values.
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW 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 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwcalcbuf(const idwmodel &s, const real_1d_array &x, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwcalcbuf(const_cast<alglib_impl::idwmodel*>(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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the IDW model at the given point, using
|
|
external buffer object (internal temporaries of IDW model are not
|
|
modified).
|
|
|
|
This function allows to use same IDW model object in different threads,
|
|
assuming that different threads use different instances of the buffer
|
|
structure.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW model, may be shared between different threads
|
|
Buf - buffer object created for this particular instance of IDW
|
|
model with idwcreatecalcbuffer().
|
|
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 idwtscalcbuf(const idwmodel &s, const idwcalcbuffer &buf, const real_1d_array &x, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwtscalcbuf(const_cast<alglib_impl::idwmodel*>(s.c_ptr()), const_cast<alglib_impl::idwcalcbuffer*>(buf.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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function fits IDW model to the dataset using current IDW construction
|
|
algorithm. A model being built and fitting report are returned.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
|
|
OUTPUT PARAMETERS:
|
|
Model - an IDW model built with current algorithm
|
|
Rep - model fitting report, fields of this structure contain
|
|
information about average fitting errors.
|
|
|
|
NOTE: although IDW-MSTAB algorithm is an interpolation method, i.e. it
|
|
tries to fit the model exactly, it can handle datasets with non-
|
|
distinct points which can not be fit exactly; in such cases least-
|
|
squares fitting is performed.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwfit(const idwbuilder &state, idwmodel &model, idwreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::idwfit(const_cast<alglib_impl::idwbuilder*>(state.c_ptr()), const_cast<alglib_impl::idwmodel*>(model.c_ptr()), const_cast<alglib_impl::idwreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RATINT) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
Barycentric interpolant.
|
|
*************************************************************************/
|
|
_barycentricinterpolant_owner::_barycentricinterpolant_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_barycentricinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::barycentricinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::barycentricinterpolant));
|
|
alglib_impl::_barycentricinterpolant_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_barycentricinterpolant_owner::_barycentricinterpolant_owner(const _barycentricinterpolant_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_barycentricinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: barycentricinterpolant copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::barycentricinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::barycentricinterpolant));
|
|
alglib_impl::_barycentricinterpolant_init_copy(p_struct, const_cast<alglib_impl::barycentricinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_barycentricinterpolant_owner& _barycentricinterpolant_owner::operator=(const _barycentricinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: barycentricinterpolant assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: barycentricinterpolant assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_barycentricinterpolant_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::barycentricinterpolant));
|
|
alglib_impl::_barycentricinterpolant_init_copy(p_struct, const_cast<alglib_impl::barycentricinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_barycentricinterpolant_owner::~_barycentricinterpolant_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_barycentricinterpolant_destroy(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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_FITSPHERE) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
Fits least squares (LS) circle (or NX-dimensional sphere) to data (a set
|
|
of points in NX-dimensional space).
|
|
|
|
Least squares circle minimizes sum of squared deviations between distances
|
|
from points to the center and some "candidate" radius, which is also
|
|
fitted to the data.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
R - radius
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.05.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspherels(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, real_1d_array &cx, double &r, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::fitspherels(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &r, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fits minimum circumscribed (MC) circle (or NX-dimensional sphere) to data
|
|
(a set of points in NX-dimensional space).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RHi - radius
|
|
|
|
NOTE: this function is an easy-to-use wrapper around more powerful "expert"
|
|
function fitspherex().
|
|
|
|
This wrapper is optimized for ease of use and stability - at the
|
|
cost of somewhat lower performance (we have to use very tight
|
|
stopping criteria for inner optimizer because we want to make sure
|
|
that it will converge on any dataset).
|
|
|
|
If you are ready to experiment with settings of "expert" function,
|
|
you can achieve ~2-4x speedup over standard "bulletproof" settings.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspheremc(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, real_1d_array &cx, double &rhi, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::fitspheremc(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rhi, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fits maximum inscribed circle (or NX-dimensional sphere) to data (a set of
|
|
points in NX-dimensional space).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RLo - radius
|
|
|
|
NOTE: this function is an easy-to-use wrapper around more powerful "expert"
|
|
function fitspherex().
|
|
|
|
This wrapper is optimized for ease of use and stability - at the
|
|
cost of somewhat lower performance (we have to use very tight
|
|
stopping criteria for inner optimizer because we want to make sure
|
|
that it will converge on any dataset).
|
|
|
|
If you are ready to experiment with settings of "expert" function,
|
|
you can achieve ~2-4x speedup over standard "bulletproof" settings.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspheremi(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, real_1d_array &cx, double &rlo, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::fitspheremi(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rlo, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fits minimum zone circle (or NX-dimensional sphere) to data (a set of
|
|
points in NX-dimensional space).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RLo - radius of inscribed circle
|
|
RHo - radius of circumscribed circle
|
|
|
|
NOTE: this function is an easy-to-use wrapper around more powerful "expert"
|
|
function fitspherex().
|
|
|
|
This wrapper is optimized for ease of use and stability - at the
|
|
cost of somewhat lower performance (we have to use very tight
|
|
stopping criteria for inner optimizer because we want to make sure
|
|
that it will converge on any dataset).
|
|
|
|
If you are ready to experiment with settings of "expert" function,
|
|
you can achieve ~2-4x speedup over standard "bulletproof" settings.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspheremz(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, real_1d_array &cx, double &rlo, double &rhi, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::fitspheremz(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rlo, &rhi, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fitting minimum circumscribed, maximum inscribed or minimum zone circles
|
|
(or NX-dimensional spheres) to data (a set of points in NX-dimensional
|
|
space).
|
|
|
|
This is expert function which allows to tweak many parameters of
|
|
underlying nonlinear solver:
|
|
* stopping criteria for inner iterations
|
|
* number of outer iterations
|
|
* penalty coefficient used to handle nonlinear constraints (we convert
|
|
unconstrained nonsmooth optimization problem ivolving max() and/or min()
|
|
operations to quadratically constrained smooth one).
|
|
|
|
You may tweak all these parameters or only some of them, leaving other
|
|
ones at their default state - just specify zero value, and solver will
|
|
fill it with appropriate default one.
|
|
|
|
These comments also include some discussion of approach used to handle
|
|
such unusual fitting problem, its stability, drawbacks of alternative
|
|
methods, and convergence properties.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
ProblemType-used to encode problem type:
|
|
* 0 for least squares circle
|
|
* 1 for minimum circumscribed circle/sphere fitting (MC)
|
|
* 2 for maximum inscribed circle/sphere fitting (MI)
|
|
* 3 for minimum zone circle fitting (difference between
|
|
Rhi and Rlo is minimized), denoted as MZ
|
|
EpsX - stopping condition for NLC optimizer:
|
|
* must be non-negative
|
|
* use 0 to choose default value (1.0E-12 is used by default)
|
|
* you may specify larger values, up to 1.0E-6, if you want
|
|
to speed-up solver; NLC solver performs several
|
|
preconditioned outer iterations, so final result
|
|
typically has precision much better than EpsX.
|
|
AULIts - number of outer iterations performed by NLC optimizer:
|
|
* must be non-negative
|
|
* use 0 to choose default value (20 is used by default)
|
|
* you may specify values smaller than 20 if you want to
|
|
speed up solver; 10 often results in good combination of
|
|
precision and speed; sometimes you may get good results
|
|
with just 6 outer iterations.
|
|
Ignored for ProblemType=0.
|
|
Penalty - penalty coefficient for NLC optimizer:
|
|
* must be non-negative
|
|
* use 0 to choose default value (1.0E6 in current version)
|
|
* it should be really large, 1.0E6...1.0E7 is a good value
|
|
to start from;
|
|
* generally, default value is good enough
|
|
Ignored for ProblemType=0.
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RLo - radius:
|
|
* for ProblemType=2,3, radius of the inscribed sphere
|
|
* for ProblemType=0 - radius of the least squares sphere
|
|
* for ProblemType=1 - zero
|
|
RHo - radius:
|
|
* for ProblemType=1,3, radius of the circumscribed sphere
|
|
* for ProblemType=0 - radius of the least squares sphere
|
|
* for ProblemType=2 - zero
|
|
|
|
NOTE: ON THE UNIQUENESS OF SOLUTIONS
|
|
|
|
ALGLIB provides solution to several related circle fitting problems: MC
|
|
(minimum circumscribed), MI (maximum inscribed) and MZ (minimum zone)
|
|
fitting, LS (least squares) fitting.
|
|
|
|
It is important to note that among these problems only MC and LS are
|
|
convex and have unique solution independently from starting point.
|
|
|
|
As for MI, it may (or may not, depending on dataset properties) have
|
|
multiple solutions, and it always has one degenerate solution C=infinity
|
|
which corresponds to infinitely large radius. Thus, there are no guarantees
|
|
that solution to MI returned by this solver will be the best one (and no
|
|
one can provide you with such guarantee because problem is NP-hard). The
|
|
only guarantee you have is that this solution is locally optimal, i.e. it
|
|
can not be improved by infinitesimally small tweaks in the parameters.
|
|
|
|
It is also possible to "run away" to infinity when started from bad
|
|
initial point located outside of point cloud (or when point cloud does not
|
|
span entire circumference/surface of the sphere).
|
|
|
|
Finally, MZ (minimum zone circle) stands somewhere between MC and MI in
|
|
stability. It is somewhat regularized by "circumscribed" term of the merit
|
|
function; however, solutions to MZ may be non-unique, and in some unlucky
|
|
cases it is also possible to "run away to infinity".
|
|
|
|
|
|
NOTE: ON THE NONLINEARLY CONSTRAINED PROGRAMMING APPROACH
|
|
|
|
The problem formulation for MC (minimum circumscribed circle; for the
|
|
sake of simplicity we omit MZ and MI here) is:
|
|
|
|
[ [ ]2 ]
|
|
min [ max [ XY[i]-C ] ]
|
|
C [ i [ ] ]
|
|
|
|
i.e. it is unconstrained nonsmooth optimization problem of finding "best"
|
|
central point, with radius R being unambiguously determined from C. In
|
|
order to move away from non-smoothness we use following reformulation:
|
|
|
|
[ ] [ ]2
|
|
min [ R ] subject to R>=0, [ XY[i]-C ] <= R^2
|
|
C,R [ ] [ ]
|
|
|
|
i.e. it becomes smooth quadratically constrained optimization problem with
|
|
linear target function. Such problem statement is 100% equivalent to the
|
|
original nonsmooth one, but much easier to approach. We solve it with
|
|
MinNLC solver provided by ALGLIB.
|
|
|
|
|
|
NOTE: ON INSTABILITY OF SEQUENTIAL LINEARIZATION APPROACH
|
|
|
|
ALGLIB has nonlinearly constrained solver which proved to be stable on
|
|
such problems. However, some authors proposed to linearize constraints in
|
|
the vicinity of current approximation (Ci,Ri) and to get next approximate
|
|
solution (Ci+1,Ri+1) as solution to linear programming problem. Obviously,
|
|
LP problems are easier than nonlinearly constrained ones.
|
|
|
|
Indeed, such approach to MC/MI/MZ resulted in ~10-20x increase in
|
|
performance (when compared with NLC solver). However, it turned out that
|
|
in some cases linearized model fails to predict correct direction for next
|
|
step and tells us that we converged to solution even when we are still 2-4
|
|
digits of precision away from it.
|
|
|
|
It is important that it is not failure of LP solver - it is failure of the
|
|
linear model; even when solved exactly, it fails to handle subtle
|
|
nonlinearities which arise near the solution. We validated it by comparing
|
|
results returned by ALGLIB linear solver with that of MATLAB.
|
|
|
|
In our experiments with linearization:
|
|
* MC failed most often, at both realistic and synthetic datasets
|
|
* MI sometimes failed, but sometimes succeeded
|
|
* MZ often succeeded; our guess is that presence of two independent sets
|
|
of constraints (one set for Rlo and another one for Rhi) and two terms
|
|
in the target function (Rlo and Rhi) regularizes task, so when linear
|
|
model fails to handle nonlinearities from Rlo, it uses Rhi as a hint
|
|
(and vice versa).
|
|
|
|
Because linearization approach failed to achieve stable results, we do not
|
|
include it in ALGLIB.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspherex(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, const ae_int_t problemtype, const double epsx, const ae_int_t aulits, const double penalty, real_1d_array &cx, double &rlo, double &rhi, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::fitspherex(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, problemtype, epsx, aulits, penalty, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rlo, &rhi, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_INTFITSERV) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_SPLINE1D) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
1-dimensional spline interpolant
|
|
*************************************************************************/
|
|
_spline1dinterpolant_owner::_spline1dinterpolant_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline1dinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::spline1dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline1dinterpolant));
|
|
alglib_impl::_spline1dinterpolant_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline1dinterpolant_owner::_spline1dinterpolant_owner(const _spline1dinterpolant_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline1dinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline1dinterpolant copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::spline1dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline1dinterpolant));
|
|
alglib_impl::_spline1dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline1dinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline1dinterpolant_owner& _spline1dinterpolant_owner::operator=(const _spline1dinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: spline1dinterpolant assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline1dinterpolant assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_spline1dinterpolant_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline1dinterpolant));
|
|
alglib_impl::_spline1dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline1dinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_spline1dinterpolant_owner::~_spline1dinterpolant_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline1dinterpolant_destroy(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()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline1dfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::spline1dfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline1dfitreport));
|
|
alglib_impl::_spline1dfitreport_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline1dfitreport_owner::_spline1dfitreport_owner(const _spline1dfitreport_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline1dfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline1dfitreport copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::spline1dfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline1dfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline1dfitreport));
|
|
alglib_impl::_spline1dfitreport_init_copy(p_struct, const_cast<alglib_impl::spline1dfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline1dfitreport_owner& _spline1dfitreport_owner::operator=(const _spline1dfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: spline1dfitreport assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline1dfitreport assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_spline1dfitreport_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline1dfitreport));
|
|
alglib_impl::_spline1dfitreport_init_copy(p_struct, const_cast<alglib_impl::spline1dfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_spline1dfitreport_owner::~_spline1dfitreport_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline1dfitreport_destroy(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()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dbuildlinear(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dbuildlinear': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dbuildcubic(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dgriddiffcubic(const real_1d_array &x, const real_1d_array &y, real_1d_array &d, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dgriddiff2cubic(const real_1d_array &x, const real_1d_array &y, real_1d_array &d1, real_1d_array &d2, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dconvcubic(const real_1d_array &x, const real_1d_array &y, const real_1d_array &x2, real_1d_array &y2, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dbuildcatmullrom(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t boundtype;
|
|
double tension;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dbuildhermite(const real_1d_array &x, const real_1d_array &y, const real_1d_array &d, spline1dinterpolant &c, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()) || (x.length()!=d.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dbuildhermite': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dbuildakima(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dbuildakima': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fitting by smoothing (penalized) cubic spline.
|
|
|
|
This function approximates N scattered points (some of X[] may be equal to
|
|
each other) by cubic spline with M nodes at equidistant grid spanning
|
|
interval [min(x,xc),max(x,xc)].
|
|
|
|
The problem is regularized by adding nonlinearity penalty to usual least
|
|
squares penalty function:
|
|
|
|
MERIT_FUNC = F_LS + F_NL
|
|
|
|
where F_LS is a least squares error term, and F_NL is a nonlinearity
|
|
penalty which is roughly proportional to LambdaNS*integral{ S''(x)^2*dx }.
|
|
Algorithm applies automatic renormalization of F_NL which makes penalty
|
|
term roughly invariant to scaling of X[] and changes in M.
|
|
|
|
This function is a new edition of penalized regression spline fitting,
|
|
a fast and compact one which needs much less resources that its previous
|
|
version: just O(maxMN) memory and O(maxMN*log(maxMN)) time.
|
|
|
|
NOTE: it is OK to run this function with both M<<N and M>>N; say, it is
|
|
possible to process 100 points with 1000-node spline.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y are processed
|
|
* if not given, automatically determined from lengths
|
|
M - number of basis functions ( = number_of_nodes), M>=4.
|
|
LambdaNS - LambdaNS>=0, regularization constant passed by user.
|
|
It penalizes nonlinearity in the regression spline.
|
|
Possible values to start from are 0.00001, 0.1, 1
|
|
|
|
OUTPUT PARAMETERS:
|
|
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
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 27.08.2019 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfit(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t m, const double lambdans, spline1dinterpolant &s, spline1dfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline1dfit(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, lambdans, 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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
Fitting by smoothing (penalized) cubic spline.
|
|
|
|
This function approximates N scattered points (some of X[] may be equal to
|
|
each other) by cubic spline with M nodes at equidistant grid spanning
|
|
interval [min(x,xc),max(x,xc)].
|
|
|
|
The problem is regularized by adding nonlinearity penalty to usual least
|
|
squares penalty function:
|
|
|
|
MERIT_FUNC = F_LS + F_NL
|
|
|
|
where F_LS is a least squares error term, and F_NL is a nonlinearity
|
|
penalty which is roughly proportional to LambdaNS*integral{ S''(x)^2*dx }.
|
|
Algorithm applies automatic renormalization of F_NL which makes penalty
|
|
term roughly invariant to scaling of X[] and changes in M.
|
|
|
|
This function is a new edition of penalized regression spline fitting,
|
|
a fast and compact one which needs much less resources that its previous
|
|
version: just O(maxMN) memory and O(maxMN*log(maxMN)) time.
|
|
|
|
NOTE: it is OK to run this function with both M<<N and M>>N; say, it is
|
|
possible to process 100 points with 1000-node spline.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y are processed
|
|
* if not given, automatically determined from lengths
|
|
M - number of basis functions ( = number_of_nodes), M>=4.
|
|
LambdaNS - LambdaNS>=0, regularization constant passed by user.
|
|
It penalizes nonlinearity in the regression spline.
|
|
Possible values to start from are 0.00001, 0.1, 1
|
|
|
|
OUTPUT PARAMETERS:
|
|
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
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 27.08.2019 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dfit(const real_1d_array &x, const real_1d_array &y, const ae_int_t m, const double lambdans, spline1dinterpolant &s, spline1dfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dfit': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline1dfit(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, lambdans, 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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void spline1dbuildmonotone(const real_1d_array &x, const real_1d_array &y, spline1dinterpolant &c, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dbuildmonotone': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_PARAMETRIC) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
Parametric spline inteprolant: 2-dimensional curve.
|
|
|
|
You should not try to access its members directly - use PSpline2XXXXXXXX()
|
|
functions instead.
|
|
*************************************************************************/
|
|
_pspline2interpolant_owner::_pspline2interpolant_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_pspline2interpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::pspline2interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline2interpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::pspline2interpolant));
|
|
alglib_impl::_pspline2interpolant_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_pspline2interpolant_owner::_pspline2interpolant_owner(const _pspline2interpolant_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_pspline2interpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: pspline2interpolant copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::pspline2interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline2interpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::pspline2interpolant));
|
|
alglib_impl::_pspline2interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline2interpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_pspline2interpolant_owner& _pspline2interpolant_owner::operator=(const _pspline2interpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: pspline2interpolant assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: pspline2interpolant assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_pspline2interpolant_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::pspline2interpolant));
|
|
alglib_impl::_pspline2interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline2interpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_pspline2interpolant_owner::~_pspline2interpolant_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_pspline2interpolant_destroy(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()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_pspline3interpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::pspline3interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline3interpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::pspline3interpolant));
|
|
alglib_impl::_pspline3interpolant_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_pspline3interpolant_owner::_pspline3interpolant_owner(const _pspline3interpolant_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_pspline3interpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: pspline3interpolant copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::pspline3interpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::pspline3interpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::pspline3interpolant));
|
|
alglib_impl::_pspline3interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline3interpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_pspline3interpolant_owner& _pspline3interpolant_owner::operator=(const _pspline3interpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: pspline3interpolant assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: pspline3interpolant assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_pspline3interpolant_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::pspline3interpolant));
|
|
alglib_impl::_pspline3interpolant_init_copy(p_struct, const_cast<alglib_impl::pspline3interpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_pspline3interpolant_owner::~_pspline3interpolant_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_pspline3interpolant_destroy(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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine fits piecewise linear curve to points with Ramer-Douglas-
|
|
Peucker algorithm. This function performs PARAMETRIC fit, i.e. it can be
|
|
used to fit curves like circles.
|
|
|
|
On input it accepts dataset which describes parametric multidimensional
|
|
curve X(t), with X being vector, and t taking values in [0,N), where N is
|
|
a number of points in dataset. As result, it returns reduced dataset X2,
|
|
which can be used to build parametric curve X2(t), which approximates
|
|
X(t) with desired precision (or has specified number of sections).
|
|
|
|
|
|
INPUT PARAMETERS:
|
|
X - array of multidimensional points:
|
|
* at least N elements, leading N elements are used if more
|
|
than N elements were specified
|
|
* order of points is IMPORTANT because it is parametric
|
|
fit
|
|
* each row of array is one point which has D coordinates
|
|
N - number of elements in X
|
|
D - number of dimensions (elements per row of X)
|
|
StopM - stopping condition - desired number of sections:
|
|
* at most M sections are generated by this function
|
|
* less than M sections can be generated if we have N<M
|
|
(or some X are non-distinct).
|
|
* zero StopM means that algorithm does not stop after
|
|
achieving some pre-specified section count
|
|
StopEps - stopping condition - desired precision:
|
|
* algorithm stops after error in each section is at most Eps
|
|
* zero Eps means that algorithm does not stop after
|
|
achieving some pre-specified precision
|
|
|
|
OUTPUT PARAMETERS:
|
|
X2 - array of corner points for piecewise approximation,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
Idx2 - array of indexes (parameter values):
|
|
* has length NSections+1 or zero (for NSections=0).
|
|
* each element of Idx2 corresponds to same-numbered
|
|
element of X2
|
|
* each element of Idx2 is index of corresponding element
|
|
of X2 at original array X, i.e. I-th row of X2 is
|
|
Idx2[I]-th row of X.
|
|
* elements of Idx2 can be treated as parameter values
|
|
which should be used when building new parametric curve
|
|
* Idx2[0]=0, Idx2[NSections]=N-1
|
|
NSections- number of sections found by algorithm, NSections<=M,
|
|
NSections can be zero for degenerate datasets
|
|
(N<=1 or all X[] are non-distinct).
|
|
|
|
NOTE: algorithm stops after:
|
|
a) dividing curve into StopM sections
|
|
b) achieving required precision StopEps
|
|
c) dividing curve into N-1 sections
|
|
If both StopM and StopEps are non-zero, algorithm is stopped by the
|
|
FIRST criterion which is satisfied. In case both StopM and StopEps
|
|
are zero, algorithm stops because of (c).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void parametricrdpfixed(const real_2d_array &x, const ae_int_t n, const ae_int_t d, const ae_int_t stopm, const double stopeps, real_2d_array &x2, integer_1d_array &idx2, ae_int_t &nsections, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::parametricrdpfixed(const_cast<alglib_impl::ae_matrix*>(x.c_ptr()), n, d, stopm, stopeps, const_cast<alglib_impl::ae_matrix*>(x2.c_ptr()), const_cast<alglib_impl::ae_vector*>(idx2.c_ptr()), &nsections, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_SPLINE3D) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
3-dimensional spline inteprolant
|
|
*************************************************************************/
|
|
_spline3dinterpolant_owner::_spline3dinterpolant_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline3dinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::spline3dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline3dinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline3dinterpolant));
|
|
alglib_impl::_spline3dinterpolant_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline3dinterpolant_owner::_spline3dinterpolant_owner(const _spline3dinterpolant_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline3dinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline3dinterpolant copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::spline3dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline3dinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline3dinterpolant));
|
|
alglib_impl::_spline3dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline3dinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline3dinterpolant_owner& _spline3dinterpolant_owner::operator=(const _spline3dinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: spline3dinterpolant assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline3dinterpolant assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_spline3dinterpolant_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline3dinterpolant));
|
|
alglib_impl::_spline3dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline3dinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_spline3dinterpolant_owner::~_spline3dinterpolant_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline3dinterpolant_destroy(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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_POLINT) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void polynomialcheb2bar(const real_1d_array &t, const double a, const double b, barycentricinterpolant &p, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = t.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void polynomialbar2pow(const barycentricinterpolant &p, real_1d_array &a, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
double c;
|
|
double s;
|
|
|
|
c = 0;
|
|
s = 1;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void polynomialpow2bar(const real_1d_array &a, barycentricinterpolant &p, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void polynomialbuild(const real_1d_array &x, const real_1d_array &y, barycentricinterpolant &p, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'polynomialbuild': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void polynomialbuildeqdist(const double a, const double b, const real_1d_array &y, barycentricinterpolant &p, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = y.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void polynomialbuildcheb1(const double a, const double b, const real_1d_array &y, barycentricinterpolant &p, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = y.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void polynomialbuildcheb2(const double a, const double b, const real_1d_array &y, barycentricinterpolant &p, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = y.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
double polynomialcalceqdist(const double a, const double b, const real_1d_array &f, const double t, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = f.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
double polynomialcalccheb1(const double a, const double b, const real_1d_array &f, const double t, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = f.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
double polynomialcalccheb2(const double a, const double b, const real_1d_array &f, const double t, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = f.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_LSFIT) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
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()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_polynomialfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::polynomialfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::polynomialfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::polynomialfitreport));
|
|
alglib_impl::_polynomialfitreport_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_polynomialfitreport_owner::_polynomialfitreport_owner(const _polynomialfitreport_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_polynomialfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: polynomialfitreport copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::polynomialfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::polynomialfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::polynomialfitreport));
|
|
alglib_impl::_polynomialfitreport_init_copy(p_struct, const_cast<alglib_impl::polynomialfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_polynomialfitreport_owner& _polynomialfitreport_owner::operator=(const _polynomialfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: polynomialfitreport assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: polynomialfitreport assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_polynomialfitreport_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::polynomialfitreport));
|
|
alglib_impl::_polynomialfitreport_init_copy(p_struct, const_cast<alglib_impl::polynomialfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_polynomialfitreport_owner::~_polynomialfitreport_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_polynomialfitreport_destroy(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()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_barycentricfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::barycentricfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::barycentricfitreport));
|
|
alglib_impl::_barycentricfitreport_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_barycentricfitreport_owner::_barycentricfitreport_owner(const _barycentricfitreport_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_barycentricfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: barycentricfitreport copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::barycentricfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::barycentricfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::barycentricfitreport));
|
|
alglib_impl::_barycentricfitreport_init_copy(p_struct, const_cast<alglib_impl::barycentricfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_barycentricfitreport_owner& _barycentricfitreport_owner::operator=(const _barycentricfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: barycentricfitreport assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: barycentricfitreport assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_barycentricfitreport_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::barycentricfitreport));
|
|
alglib_impl::_barycentricfitreport_init_copy(p_struct, const_cast<alglib_impl::barycentricfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_barycentricfitreport_owner::~_barycentricfitreport_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_barycentricfitreport_destroy(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()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_lsfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::lsfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::lsfitreport));
|
|
alglib_impl::_lsfitreport_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_lsfitreport_owner::_lsfitreport_owner(const _lsfitreport_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_lsfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: lsfitreport copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::lsfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::lsfitreport));
|
|
alglib_impl::_lsfitreport_init_copy(p_struct, const_cast<alglib_impl::lsfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_lsfitreport_owner& _lsfitreport_owner::operator=(const _lsfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: lsfitreport assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: lsfitreport assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_lsfitreport_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::lsfitreport));
|
|
alglib_impl::_lsfitreport_init_copy(p_struct, const_cast<alglib_impl::lsfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_lsfitreport_owner::~_lsfitreport_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_lsfitreport_destroy(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()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_lsfitstate_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::lsfitstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitstate), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::lsfitstate));
|
|
alglib_impl::_lsfitstate_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_lsfitstate_owner::_lsfitstate_owner(const _lsfitstate_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_lsfitstate_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: lsfitstate copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::lsfitstate*)alglib_impl::ae_malloc(sizeof(alglib_impl::lsfitstate), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::lsfitstate));
|
|
alglib_impl::_lsfitstate_init_copy(p_struct, const_cast<alglib_impl::lsfitstate*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_lsfitstate_owner& _lsfitstate_owner::operator=(const _lsfitstate_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: lsfitstate assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: lsfitstate assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_lsfitstate_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::lsfitstate));
|
|
alglib_impl::_lsfitstate_init_copy(p_struct, const_cast<alglib_impl::lsfitstate*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_lsfitstate_owner::~_lsfitstate_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_lsfitstate_destroy(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()
|
|
{
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine fits piecewise linear curve to points with Ramer-Douglas-
|
|
Peucker algorithm, which stops after generating specified number of linear
|
|
sections.
|
|
|
|
IMPORTANT:
|
|
* it does NOT perform least-squares fitting; it builds curve, but this
|
|
curve does not minimize some least squares metric. See description of
|
|
RDP algorithm (say, in Wikipedia) for more details on WHAT is performed.
|
|
* this function does NOT work with parametric curves (i.e. curves which
|
|
can be represented as {X(t),Y(t)}. It works with curves which can be
|
|
represented as Y(X). Thus, it is impossible to model figures like
|
|
circles with this functions.
|
|
If you want to work with parametric curves, you should use
|
|
ParametricRDPFixed() function provided by "Parametric" subpackage of
|
|
"Interpolation" package.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array of X-coordinates:
|
|
* at least N elements
|
|
* can be unordered (points are automatically sorted)
|
|
* this function may accept non-distinct X (see below for
|
|
more information on handling of such inputs)
|
|
Y - array of Y-coordinates:
|
|
* at least N elements
|
|
N - number of elements in X/Y
|
|
M - desired number of sections:
|
|
* at most M sections are generated by this function
|
|
* less than M sections can be generated if we have N<M
|
|
(or some X are non-distinct).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X2 - X-values of corner points for piecewise approximation,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
Y2 - Y-values of corner points,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
NSections- number of sections found by algorithm, NSections<=M,
|
|
NSections can be zero for degenerate datasets
|
|
(N<=1 or all X[] are non-distinct).
|
|
|
|
NOTE: X2/Y2 are ordered arrays, i.e. (X2[0],Y2[0]) is a first point of
|
|
curve, (X2[NSection-1],Y2[NSection-1]) is the last point.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lstfitpiecewiselinearrdpfixed(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const ae_int_t m, real_1d_array &x2, real_1d_array &y2, ae_int_t &nsections, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lstfitpiecewiselinearrdpfixed(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, m, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), &nsections, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine fits piecewise linear curve to points with Ramer-Douglas-
|
|
Peucker algorithm, which stops after achieving desired precision.
|
|
|
|
IMPORTANT:
|
|
* it performs non-least-squares fitting; it builds curve, but this curve
|
|
does not minimize some least squares metric. See description of RDP
|
|
algorithm (say, in Wikipedia) for more details on WHAT is performed.
|
|
* this function does NOT work with parametric curves (i.e. curves which
|
|
can be represented as {X(t),Y(t)}. It works with curves which can be
|
|
represented as Y(X). Thus, it is impossible to model figures like circles
|
|
with this functions.
|
|
If you want to work with parametric curves, you should use
|
|
ParametricRDPFixed() function provided by "Parametric" subpackage of
|
|
"Interpolation" package.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array of X-coordinates:
|
|
* at least N elements
|
|
* can be unordered (points are automatically sorted)
|
|
* this function may accept non-distinct X (see below for
|
|
more information on handling of such inputs)
|
|
Y - array of Y-coordinates:
|
|
* at least N elements
|
|
N - number of elements in X/Y
|
|
Eps - positive number, desired precision.
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
X2 - X-values of corner points for piecewise approximation,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
Y2 - Y-values of corner points,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
NSections- number of sections found by algorithm,
|
|
NSections can be zero for degenerate datasets
|
|
(N<=1 or all X[] are non-distinct).
|
|
|
|
NOTE: X2/Y2 are ordered arrays, i.e. (X2[0],Y2[0]) is a first point of
|
|
curve, (X2[NSection-1],Y2[NSection-1]) is the last point.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lstfitpiecewiselinearrdp(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const double eps, real_1d_array &x2, real_1d_array &y2, ae_int_t &nsections, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lstfitpiecewiselinearrdp(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, eps, const_cast<alglib_impl::ae_vector*>(x2.c_ptr()), const_cast<alglib_impl::ae_vector*>(y2.c_ptr()), &nsections, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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()
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
|
|
-- 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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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()
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 10.12.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'polynomialfit': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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()
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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()
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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.
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t k;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'polynomialfitwc': looks like one of arguments has wrong size");
|
|
if( (xc.length()!=yc.length()) || (xc.length()!=dc.length()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
This function calculates value of four-parameter logistic (4PL) model at
|
|
specified point X. 4PL model has following form:
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B))
|
|
|
|
INPUT PARAMETERS:
|
|
X - current point, X>=0:
|
|
* zero X is correctly handled even for B<=0
|
|
* negative X results in exception.
|
|
A, B, C, D- parameters of 4PL model:
|
|
* A is unconstrained
|
|
* B is unconstrained; zero or negative values are handled
|
|
correctly.
|
|
* C>0, non-positive value results in exception
|
|
* D is unconstrained
|
|
|
|
RESULT:
|
|
model value at X
|
|
|
|
NOTE: if B=0, denominator is assumed to be equal to 2.0 even for zero X
|
|
(strictly speaking, 0^0 is undefined).
|
|
|
|
NOTE: this function also throws exception if all input parameters are
|
|
correct, but overflow was detected during calculations.
|
|
|
|
NOTE: this function performs a lot of checks; if you need really high
|
|
performance, consider evaluating model yourself, without checking
|
|
for degenerate cases.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.05.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double logisticcalc4(const double x, const double a, const double b, const double c, const double d, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::logisticcalc4(x, a, b, c, d, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates value of five-parameter logistic (5PL) model at
|
|
specified point X. 5PL model has following form:
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G)
|
|
|
|
INPUT PARAMETERS:
|
|
X - current point, X>=0:
|
|
* zero X is correctly handled even for B<=0
|
|
* negative X results in exception.
|
|
A, B, C, D, G- parameters of 5PL model:
|
|
* A is unconstrained
|
|
* B is unconstrained; zero or negative values are handled
|
|
correctly.
|
|
* C>0, non-positive value results in exception
|
|
* D is unconstrained
|
|
* G>0, non-positive value results in exception
|
|
|
|
RESULT:
|
|
model value at X
|
|
|
|
NOTE: if B=0, denominator is assumed to be equal to Power(2.0,G) even for
|
|
zero X (strictly speaking, 0^0 is undefined).
|
|
|
|
NOTE: this function also throws exception if all input parameters are
|
|
correct, but overflow was detected during calculations.
|
|
|
|
NOTE: this function performs a lot of checks; if you need really high
|
|
performance, consider evaluating model yourself, without checking
|
|
for degenerate cases.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.05.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double logisticcalc5(const double x, const double a, const double b, const double c, const double d, const double g, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::logisticcalc5(x, a, b, c, d, g, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function fits four-parameter logistic (4PL) model to data provided
|
|
by user. 4PL model has following form:
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B))
|
|
|
|
Here:
|
|
* A, D - unconstrained (see LogisticFit4EC() for constrained 4PL)
|
|
* B>=0
|
|
* C>0
|
|
|
|
IMPORTANT: output of this function is constrained in such way that B>0.
|
|
Because 4PL model is symmetric with respect to B, there is no
|
|
need to explore B<0. Constraining B makes algorithm easier
|
|
to stabilize and debug.
|
|
Users who for some reason prefer to work with negative B's
|
|
should transform output themselves (swap A and D, replace B by
|
|
-B).
|
|
|
|
4PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot".
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints. Results from the previous round are used as initial guess.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A, B, C, D- parameters of 4PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for stability reasons the B parameter is restricted by [1/1000,1000]
|
|
range. It prevents algorithm from making trial steps deep into the
|
|
area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc4() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit4(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, double &a, double &b, double &c, double &d, lsfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::logisticfit4(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, &a, &b, &c, &d, const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function fits four-parameter logistic (4PL) model to data provided
|
|
by user, with optional constraints on parameters A and D. 4PL model has
|
|
following form:
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B))
|
|
|
|
Here:
|
|
* A, D - with optional equality constraints
|
|
* B>=0
|
|
* C>0
|
|
|
|
IMPORTANT: output of this function is constrained in such way that B>0.
|
|
Because 4PL model is symmetric with respect to B, there is no
|
|
need to explore B<0. Constraining B makes algorithm easier
|
|
to stabilize and debug.
|
|
Users who for some reason prefer to work with negative B's
|
|
should transform output themselves (swap A and D, replace B by
|
|
-B).
|
|
|
|
4PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot".
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints. Results from the previous round are used as initial guess.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
CnstrLeft- optional equality constraint for model value at the left
|
|
boundary (at X=0). Specify NAN (Not-a-Number) if you do
|
|
not need constraint on the model value at X=0 (in C++ you
|
|
can pass alglib::fp_nan as parameter, in C# it will be
|
|
Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
CnstrRight- optional equality constraint for model value at X=infinity.
|
|
Specify NAN (Not-a-Number) if you do not need constraint
|
|
on the model value (in C++ you can pass alglib::fp_nan as
|
|
parameter, in C# it will be Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A, B, C, D- parameters of 4PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for stability reasons the B parameter is restricted by [1/1000,1000]
|
|
range. It prevents algorithm from making trial steps deep into the
|
|
area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc4() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
EQUALITY CONSTRAINTS ON PARAMETERS
|
|
|
|
4PL/5PL solver supports equality constraints on model values at the left
|
|
boundary (X=0) and right boundary (X=infinity). These constraints are
|
|
completely optional and you can specify both of them, only one - or no
|
|
constraints at all.
|
|
|
|
Parameter CnstrLeft contains left constraint (or NAN for unconstrained
|
|
fitting), and CnstrRight contains right one. For 4PL, left constraint
|
|
ALWAYS corresponds to parameter A, and right one is ALWAYS constraint on
|
|
D. That's because 4PL model is normalized in such way that B>=0.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit4ec(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const double cnstrleft, const double cnstrright, double &a, double &b, double &c, double &d, lsfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::logisticfit4ec(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, cnstrleft, cnstrright, &a, &b, &c, &d, const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function fits five-parameter logistic (5PL) model to data provided
|
|
by user. 5PL model has following form:
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G)
|
|
|
|
Here:
|
|
* A, D - unconstrained
|
|
* B - unconstrained
|
|
* C>0
|
|
* G>0
|
|
|
|
IMPORTANT: unlike in 4PL fitting, output of this function is NOT
|
|
constrained in such way that B is guaranteed to be positive.
|
|
Furthermore, unlike 4PL, 5PL model is NOT symmetric with
|
|
respect to B, so you can NOT transform model to equivalent one,
|
|
with B having desired sign (>0 or <0).
|
|
|
|
5PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot". Parameter G is
|
|
fixed at G=1.
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints on B and C, but with G still equal to 1. Results from the
|
|
previous round are used as initial guess.
|
|
* third Levenberg-Marquardt round relaxes constraints on G and tries two
|
|
different models - one with B>0 and one with B<0.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A,B,C,D,G- parameters of 5PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for better stability B parameter is restricted by [+-1/1000,+-1000]
|
|
range, and G is restricted by [1/10,10] range. It prevents algorithm
|
|
from making trial steps deep into the area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc5() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit5(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, double &a, double &b, double &c, double &d, double &g, lsfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::logisticfit5(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, &a, &b, &c, &d, &g, const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function fits five-parameter logistic (5PL) model to data provided
|
|
by user, subject to optional equality constraints on parameters A and D.
|
|
5PL model has following form:
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G)
|
|
|
|
Here:
|
|
* A, D - with optional equality constraints
|
|
* B - unconstrained
|
|
* C>0
|
|
* G>0
|
|
|
|
IMPORTANT: unlike in 4PL fitting, output of this function is NOT
|
|
constrained in such way that B is guaranteed to be positive.
|
|
Furthermore, unlike 4PL, 5PL model is NOT symmetric with
|
|
respect to B, so you can NOT transform model to equivalent one,
|
|
with B having desired sign (>0 or <0).
|
|
|
|
5PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot". Parameter G is
|
|
fixed at G=1.
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints on B and C, but with G still equal to 1. Results from the
|
|
previous round are used as initial guess.
|
|
* third Levenberg-Marquardt round relaxes constraints on G and tries two
|
|
different models - one with B>0 and one with B<0.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
CnstrLeft- optional equality constraint for model value at the left
|
|
boundary (at X=0). Specify NAN (Not-a-Number) if you do
|
|
not need constraint on the model value at X=0 (in C++ you
|
|
can pass alglib::fp_nan as parameter, in C# it will be
|
|
Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
CnstrRight- optional equality constraint for model value at X=infinity.
|
|
Specify NAN (Not-a-Number) if you do not need constraint
|
|
on the model value (in C++ you can pass alglib::fp_nan as
|
|
parameter, in C# it will be Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A,B,C,D,G- parameters of 5PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for better stability B parameter is restricted by [+-1/1000,+-1000]
|
|
range, and G is restricted by [1/10,10] range. It prevents algorithm
|
|
from making trial steps deep into the area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc5() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
EQUALITY CONSTRAINTS ON PARAMETERS
|
|
|
|
5PL solver supports equality constraints on model values at the left
|
|
boundary (X=0) and right boundary (X=infinity). These constraints are
|
|
completely optional and you can specify both of them, only one - or no
|
|
constraints at all.
|
|
|
|
Parameter CnstrLeft contains left constraint (or NAN for unconstrained
|
|
fitting), and CnstrRight contains right one.
|
|
|
|
Unlike 4PL one, 5PL model is NOT symmetric with respect to change in sign
|
|
of B. Thus, negative B's are possible, and left constraint may constrain
|
|
parameter A (for positive B's) - or parameter D (for negative B's).
|
|
Similarly changes meaning of right constraint.
|
|
|
|
You do not have to decide what parameter to constrain - algorithm will
|
|
automatically determine correct parameters as fitting progresses. However,
|
|
question highlighted above is important when you interpret fitting results.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit5ec(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const double cnstrleft, const double cnstrright, double &a, double &b, double &c, double &d, double &g, lsfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::logisticfit5ec(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, cnstrleft, cnstrright, &a, &b, &c, &d, &g, const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This is "expert" 4PL/5PL fitting function, which can be used if you need
|
|
better control over fitting process than provided by LogisticFit4() or
|
|
LogisticFit5().
|
|
|
|
This function fits model of the form
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B)) (4PL model)
|
|
|
|
or
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G) (5PL model)
|
|
|
|
Here:
|
|
* A, D - unconstrained
|
|
* B>=0 for 4PL, unconstrained for 5PL
|
|
* C>0
|
|
* G>0 (if present)
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
CnstrLeft- optional equality constraint for model value at the left
|
|
boundary (at X=0). Specify NAN (Not-a-Number) if you do
|
|
not need constraint on the model value at X=0 (in C++ you
|
|
can pass alglib::fp_nan as parameter, in C# it will be
|
|
Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
CnstrRight- optional equality constraint for model value at X=infinity.
|
|
Specify NAN (Not-a-Number) if you do not need constraint
|
|
on the model value (in C++ you can pass alglib::fp_nan as
|
|
parameter, in C# it will be Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
Is4PL - whether 4PL or 5PL models are fitted
|
|
LambdaV - regularization coefficient, LambdaV>=0.
|
|
Set it to zero unless you know what you are doing.
|
|
EpsX - stopping condition (step size), EpsX>=0.
|
|
Zero value means that small step is automatically chosen.
|
|
See notes below for more information.
|
|
RsCnt - number of repeated restarts from random points. 4PL/5PL
|
|
models are prone to problem of bad local extrema. Utilizing
|
|
multiple random restarts allows us to improve algorithm
|
|
convergence.
|
|
RsCnt>=0.
|
|
Zero value means that function automatically choose small
|
|
amount of restarts (recommended).
|
|
|
|
OUTPUT PARAMETERS:
|
|
A, B, C, D- parameters of 4PL model
|
|
G - parameter of 5PL model; for Is4PL=True, G=1 is returned.
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for better stability B parameter is restricted by [+-1/1000,+-1000]
|
|
range, and G is restricted by [1/10,10] range. It prevents algorithm
|
|
from making trial steps deep into the area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc5() function.
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
EQUALITY CONSTRAINTS ON PARAMETERS
|
|
|
|
4PL/5PL solver supports equality constraints on model values at the left
|
|
boundary (X=0) and right boundary (X=infinity). These constraints are
|
|
completely optional and you can specify both of them, only one - or no
|
|
constraints at all.
|
|
|
|
Parameter CnstrLeft contains left constraint (or NAN for unconstrained
|
|
fitting), and CnstrRight contains right one. For 4PL, left constraint
|
|
ALWAYS corresponds to parameter A, and right one is ALWAYS constraint on
|
|
D. That's because 4PL model is normalized in such way that B>=0.
|
|
|
|
For 5PL model things are different. Unlike 4PL one, 5PL model is NOT
|
|
symmetric with respect to change in sign of B. Thus, negative B's are
|
|
possible, and left constraint may constrain parameter A (for positive B's)
|
|
- or parameter D (for negative B's). Similarly changes meaning of right
|
|
constraint.
|
|
|
|
You do not have to decide what parameter to constrain - algorithm will
|
|
automatically determine correct parameters as fitting progresses. However,
|
|
question highlighted above is important when you interpret fitting results.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit45x(const real_1d_array &x, const real_1d_array &y, const ae_int_t n, const double cnstrleft, const double cnstrright, const bool is4pl, const double lambdav, const double epsx, const ae_int_t rscnt, double &a, double &b, double &c, double &d, double &g, lsfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::logisticfit45x(const_cast<alglib_impl::ae_vector*>(x.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), n, cnstrleft, cnstrright, is4pl, lambdav, epsx, rscnt, &a, &b, &c, &d, &g, const_cast<alglib_impl::lsfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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).
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t k;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dfitcubicwc': looks like one of arguments has wrong size");
|
|
if( (xc.length()!=yc.length()) || (xc.length()!=dc.length()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t k;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dfithermitewc': looks like one of arguments has wrong size");
|
|
if( (xc.length()!=yc.length()) || (xc.length()!=dc.length()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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)
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
-- 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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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)
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dfitcubic': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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).
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
-- 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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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).
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dfithermite': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
if( (y.length()!=w.length()) || (y.length()!=fmatrix.rows()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'lsfitlinearwc': looks like one of arguments has wrong size");
|
|
if( (fmatrix.cols()!=cmatrix.cols()-1))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void lsfitlinear(const real_1d_array &y, const real_2d_array &fmatrix, ae_int_t &info, real_1d_array &c, lsfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
if( (y.length()!=fmatrix.rows()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (y.length()!=fmatrix.rows()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'lsfitlinearc': looks like one of arguments has wrong size");
|
|
if( (fmatrix.cols()!=cmatrix.cols()-1))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void lsfitcreatef(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, const double diffstep, lsfitstate &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void lsfitcreatefg(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, const bool cheapfg, lsfitstate &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void lsfitcreatewfgh(const real_2d_array &x, const real_1d_array &y, const real_1d_array &w, const real_1d_array &c, lsfitstate &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void lsfitcreatefgh(const real_2d_array &x, const real_1d_array &y, const real_1d_array &c, lsfitstate &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
ae_int_t m;
|
|
ae_int_t k;
|
|
if( (x.rows()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
Stopping conditions for nonlinear least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure which stores algorithm state
|
|
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 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 epsx, const ae_int_t maxits, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lsfitsetcond(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), epsx, maxits, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lsfitsetstpmax(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), stpmax, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lsfitsetxrep(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), needxrep, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets linear constraints for underlying optimizer
|
|
|
|
Linear constraints are inactive by default (after initial creation).
|
|
They are preserved until explicitly turned off with another SetLC() call.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure stores algorithm state
|
|
C - linear constraints, array[K,N+1].
|
|
Each row of C represents one constraint, either equality
|
|
or inequality (see below):
|
|
* first N elements correspond to coefficients,
|
|
* last element corresponds to the right part.
|
|
All elements of C (including right part) must be finite.
|
|
CT - type of constraints, array[K]:
|
|
* if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
|
|
* if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1]
|
|
* if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
|
|
K - number of equality/inequality constraints, K>=0:
|
|
* if given, only leading K elements of C/CT are used
|
|
* if not given, automatically determined from sizes of C/CT
|
|
|
|
IMPORTANT: if you have linear constraints, it is strongly recommended to
|
|
set scale of variables with lsfitsetscale(). QP solver which is
|
|
used to calculate linearly constrained steps heavily relies on
|
|
good scaling of input problems.
|
|
|
|
NOTE: linear (non-box) constraints are satisfied only approximately -
|
|
there always exists some violation due to numerical errors and
|
|
algorithmic limitations.
|
|
|
|
NOTE: general linear constraints add significant overhead to solution
|
|
process. Although solver performs roughly same amount of iterations
|
|
(when compared with similar box-only constrained problem), each
|
|
iteration now involves solution of linearly constrained QP
|
|
subproblem, which requires ~3-5 times more Cholesky decompositions.
|
|
Thus, if you can reformulate your problem in such way this it has
|
|
only box constraints, it may be beneficial to do so.
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetlc(const lsfitstate &state, const real_2d_array &c, const integer_1d_array &ct, const ae_int_t k, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lsfitsetlc(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), const_cast<alglib_impl::ae_matrix*>(c.c_ptr()), const_cast<alglib_impl::ae_vector*>(ct.c_ptr()), k, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets linear constraints for underlying optimizer
|
|
|
|
Linear constraints are inactive by default (after initial creation).
|
|
They are preserved until explicitly turned off with another SetLC() call.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure stores algorithm state
|
|
C - linear constraints, array[K,N+1].
|
|
Each row of C represents one constraint, either equality
|
|
or inequality (see below):
|
|
* first N elements correspond to coefficients,
|
|
* last element corresponds to the right part.
|
|
All elements of C (including right part) must be finite.
|
|
CT - type of constraints, array[K]:
|
|
* if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
|
|
* if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1]
|
|
* if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
|
|
K - number of equality/inequality constraints, K>=0:
|
|
* if given, only leading K elements of C/CT are used
|
|
* if not given, automatically determined from sizes of C/CT
|
|
|
|
IMPORTANT: if you have linear constraints, it is strongly recommended to
|
|
set scale of variables with lsfitsetscale(). QP solver which is
|
|
used to calculate linearly constrained steps heavily relies on
|
|
good scaling of input problems.
|
|
|
|
NOTE: linear (non-box) constraints are satisfied only approximately -
|
|
there always exists some violation due to numerical errors and
|
|
algorithmic limitations.
|
|
|
|
NOTE: general linear constraints add significant overhead to solution
|
|
process. Although solver performs roughly same amount of iterations
|
|
(when compared with similar box-only constrained problem), each
|
|
iteration now involves solution of linearly constrained QP
|
|
subproblem, which requires ~3-5 times more Cholesky decompositions.
|
|
Thus, if you can reformulate your problem in such way this it has
|
|
only box constraints, it may be beneficial to do so.
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void lsfitsetlc(const lsfitstate &state, const real_2d_array &c, const integer_1d_array &ct, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t k;
|
|
if( (c.rows()!=ct.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'lsfitsetlc': looks like one of arguments has wrong size");
|
|
k = c.rows();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lsfitsetlc(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), const_cast<alglib_impl::ae_matrix*>(c.c_ptr()), const_cast<alglib_impl::ae_vector*>(ct.c_ptr()), k, &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
|
|
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,
|
|
const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::ae_assert(func!=NULL, "ALGLIB: error in 'lsfitfit()' (func is NULL)", &_alglib_env_state);
|
|
while( alglib_impl::lsfititeration(state.c_ptr(), &_alglib_env_state) )
|
|
{
|
|
_ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
|
|
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;
|
|
}
|
|
goto lbl_no_callback;
|
|
_ALGLIB_CALLBACK_EXCEPTION_GUARD_END
|
|
lbl_no_callback:
|
|
alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'lsfitfit' (some derivatives were not provided?)", &_alglib_env_state);
|
|
}
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
}
|
|
|
|
|
|
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,
|
|
const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::ae_assert(func!=NULL, "ALGLIB: error in 'lsfitfit()' (func is NULL)", &_alglib_env_state);
|
|
alglib_impl::ae_assert(grad!=NULL, "ALGLIB: error in 'lsfitfit()' (grad is NULL)", &_alglib_env_state);
|
|
while( alglib_impl::lsfititeration(state.c_ptr(), &_alglib_env_state) )
|
|
{
|
|
_ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
|
|
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;
|
|
}
|
|
goto lbl_no_callback;
|
|
_ALGLIB_CALLBACK_EXCEPTION_GUARD_END
|
|
lbl_no_callback:
|
|
alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'lsfitfit' (some derivatives were not provided?)", &_alglib_env_state);
|
|
}
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
}
|
|
|
|
|
|
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,
|
|
const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::ae_assert(func!=NULL, "ALGLIB: error in 'lsfitfit()' (func is NULL)", &_alglib_env_state);
|
|
alglib_impl::ae_assert(grad!=NULL, "ALGLIB: error in 'lsfitfit()' (grad is NULL)", &_alglib_env_state);
|
|
alglib_impl::ae_assert(hess!=NULL, "ALGLIB: error in 'lsfitfit()' (hess is NULL)", &_alglib_env_state);
|
|
while( alglib_impl::lsfititeration(state.c_ptr(), &_alglib_env_state) )
|
|
{
|
|
_ALGLIB_CALLBACK_EXCEPTION_GUARD_BEGIN
|
|
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;
|
|
}
|
|
goto lbl_no_callback;
|
|
_ALGLIB_CALLBACK_EXCEPTION_GUARD_END
|
|
lbl_no_callback:
|
|
alglib_impl::ae_assert(ae_false, "ALGLIB: error in 'lsfitfit' (some derivatives were not provided?)", &_alglib_env_state);
|
|
}
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares fitting results.
|
|
|
|
Called after return from LSFitFit().
|
|
|
|
INPUT PARAMETERS:
|
|
State - algorithm state
|
|
|
|
OUTPUT PARAMETERS:
|
|
Info - completion code:
|
|
* -8 optimizer detected NAN/INF in the target
|
|
function and/or gradient
|
|
* -7 gradient verification failed.
|
|
See LSFitSetGradientCheck() for more information.
|
|
* -3 inconsistent constraints
|
|
* 2 relative step is no more than EpsX.
|
|
* 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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::lsfitsetgradientcheck(const_cast<alglib_impl::lsfitstate*>(state.c_ptr()), teststep, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RBFV2) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_SPLINE2D) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
2-dimensional spline inteprolant
|
|
*************************************************************************/
|
|
_spline2dinterpolant_owner::_spline2dinterpolant_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::spline2dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dinterpolant));
|
|
alglib_impl::_spline2dinterpolant_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline2dinterpolant_owner::_spline2dinterpolant_owner(const _spline2dinterpolant_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dinterpolant_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline2dinterpolant copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::spline2dinterpolant*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dinterpolant), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dinterpolant));
|
|
alglib_impl::_spline2dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline2dinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline2dinterpolant_owner& _spline2dinterpolant_owner::operator=(const _spline2dinterpolant_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: spline2dinterpolant assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline2dinterpolant assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_spline2dinterpolant_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dinterpolant));
|
|
alglib_impl::_spline2dinterpolant_init_copy(p_struct, const_cast<alglib_impl::spline2dinterpolant*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_spline2dinterpolant_owner::~_spline2dinterpolant_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dinterpolant_destroy(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()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Nonlinear least squares solver used to fit 2D splines to data
|
|
*************************************************************************/
|
|
_spline2dbuilder_owner::_spline2dbuilder_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dbuilder_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::spline2dbuilder*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dbuilder), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dbuilder));
|
|
alglib_impl::_spline2dbuilder_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline2dbuilder_owner::_spline2dbuilder_owner(const _spline2dbuilder_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dbuilder_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline2dbuilder copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::spline2dbuilder*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dbuilder), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dbuilder));
|
|
alglib_impl::_spline2dbuilder_init_copy(p_struct, const_cast<alglib_impl::spline2dbuilder*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline2dbuilder_owner& _spline2dbuilder_owner::operator=(const _spline2dbuilder_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: spline2dbuilder assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline2dbuilder assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_spline2dbuilder_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dbuilder));
|
|
alglib_impl::_spline2dbuilder_init_copy(p_struct, const_cast<alglib_impl::spline2dbuilder*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_spline2dbuilder_owner::~_spline2dbuilder_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dbuilder_destroy(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
}
|
|
|
|
alglib_impl::spline2dbuilder* _spline2dbuilder_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::spline2dbuilder* _spline2dbuilder_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::spline2dbuilder*>(p_struct);
|
|
}
|
|
spline2dbuilder::spline2dbuilder() : _spline2dbuilder_owner()
|
|
{
|
|
}
|
|
|
|
spline2dbuilder::spline2dbuilder(const spline2dbuilder &rhs):_spline2dbuilder_owner(rhs)
|
|
{
|
|
}
|
|
|
|
spline2dbuilder& spline2dbuilder::operator=(const spline2dbuilder &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_spline2dbuilder_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
spline2dbuilder::~spline2dbuilder()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Spline 2D fitting report:
|
|
rmserror RMS error
|
|
avgerror average error
|
|
maxerror maximum error
|
|
r2 coefficient of determination, R-squared, 1-RSS/TSS
|
|
*************************************************************************/
|
|
_spline2dfitreport_owner::_spline2dfitreport_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::spline2dfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dfitreport));
|
|
alglib_impl::_spline2dfitreport_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline2dfitreport_owner::_spline2dfitreport_owner(const _spline2dfitreport_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dfitreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline2dfitreport copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::spline2dfitreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::spline2dfitreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dfitreport));
|
|
alglib_impl::_spline2dfitreport_init_copy(p_struct, const_cast<alglib_impl::spline2dfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_spline2dfitreport_owner& _spline2dfitreport_owner::operator=(const _spline2dfitreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: spline2dfitreport assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: spline2dfitreport assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_spline2dfitreport_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::spline2dfitreport));
|
|
alglib_impl::_spline2dfitreport_init_copy(p_struct, const_cast<alglib_impl::spline2dfitreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_spline2dfitreport_owner::~_spline2dfitreport_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_spline2dfitreport_destroy(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
}
|
|
|
|
alglib_impl::spline2dfitreport* _spline2dfitreport_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::spline2dfitreport* _spline2dfitreport_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::spline2dfitreport*>(p_struct);
|
|
}
|
|
spline2dfitreport::spline2dfitreport() : _spline2dfitreport_owner() ,rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),maxerror(p_struct->maxerror),r2(p_struct->r2)
|
|
{
|
|
}
|
|
|
|
spline2dfitreport::spline2dfitreport(const spline2dfitreport &rhs):_spline2dfitreport_owner(rhs) ,rmserror(p_struct->rmserror),avgerror(p_struct->avgerror),maxerror(p_struct->maxerror),r2(p_struct->r2)
|
|
{
|
|
}
|
|
|
|
spline2dfitreport& spline2dfitreport::operator=(const spline2dfitreport &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_spline2dfitreport_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
spline2dfitreport::~spline2dfitreport()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 spline2dserialize(spline2dinterpolant &obj, std::string &s_out)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
alglib_impl::ae_int_t ssize;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_alloc_start(&serializer);
|
|
alglib_impl::spline2dalloc(&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::spline2dserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_assert( s_out.length()<=(size_t)ssize, "ALGLIB: serialization integrity error", &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
/*************************************************************************
|
|
This function unserializes data structure from string.
|
|
*************************************************************************/
|
|
void spline2dunserialize(const std::string &s_in, spline2dinterpolant &obj)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_ustart_str(&serializer, &s_in);
|
|
alglib_impl::spline2dunserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function serializes data structure to C++ stream.
|
|
|
|
Data stream generated by this function is same as string representation
|
|
generated by string version of serializer - alphanumeric characters,
|
|
dots, underscores, minus signs, which are grouped into words separated by
|
|
spaces and CR+LF.
|
|
|
|
We recommend you to read comments on string version of serializer to find
|
|
out more about serialization of AlGLIB objects.
|
|
*************************************************************************/
|
|
void spline2dserialize(spline2dinterpolant &obj, std::ostream &s_out)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_alloc_start(&serializer);
|
|
alglib_impl::spline2dalloc(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_get_alloc_size(&serializer); // not actually needed, but we have to ask
|
|
alglib_impl::ae_serializer_sstart_stream(&serializer, &s_out);
|
|
alglib_impl::spline2dserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
/*************************************************************************
|
|
This function unserializes data structure from stream.
|
|
*************************************************************************/
|
|
void spline2dunserialize(const std::istream &s_in, spline2dinterpolant &obj)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_ustart_stream(&serializer, &s_in);
|
|
alglib_impl::spline2dunserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the bilinear or bicubic spline at
|
|
the given point X.
|
|
|
|
Input parameters:
|
|
C - 2D spline object.
|
|
Built by spline2dbuildbilinearv or spline2dbuildbicubicv.
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y).
|
|
|
|
If you need just some specific component of vector-valued spline, you can
|
|
use spline2dcalcvi() function.
|
|
|
|
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 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcalcvbuf(const spline2dinterpolant &c, const double x, const double y, real_1d_array &f, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates specific component of vector-valued bilinear or
|
|
bicubic spline at the given point (X,Y).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
I - component index, in [0,D). An exception is generated for out
|
|
of range values.
|
|
|
|
RESULT:
|
|
value of I-th component
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline2dcalcvi(const spline2dinterpolant &c, const double x, const double y, const ae_int_t i, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::spline2dcalcvi(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), x, y, i, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates value of specific component of bilinear or
|
|
bicubic vector-valued spline and its derivatives.
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
I - component index, in [0,D)
|
|
|
|
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 spline2ddiffvi(const spline2dinterpolant &c, const double x, const double y, const ae_int_t i, double &f, double &fx, double &fy, double &fxy, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2ddiffvi(const_cast<alglib_impl::spline2dinterpolant*>(c.c_ptr()), x, y, i, &f, &fx, &fy, &fxy, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
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(const spline2dinterpolant &c, const double ax, const double bx, const double ay, const double by, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This subroutine creates least squares solver used to fit 2D splines to
|
|
irregularly sampled (scattered) data.
|
|
|
|
Solver object is used to perform spline fits as follows:
|
|
* solver object is created with spline2dbuildercreate() function
|
|
* dataset is added with spline2dbuildersetpoints() function
|
|
* fit area is chosen:
|
|
* spline2dbuildersetarea() - for user-defined area
|
|
* spline2dbuildersetareaauto() - for automatically chosen area
|
|
* number of grid nodes is chosen with spline2dbuildersetgrid()
|
|
* prior term is chosen with one of the following functions:
|
|
* spline2dbuildersetlinterm() to set linear prior
|
|
* spline2dbuildersetconstterm() to set constant prior
|
|
* spline2dbuildersetzeroterm() to set zero prior
|
|
* spline2dbuildersetuserterm() to set user-defined constant prior
|
|
* solver algorithm is chosen with either:
|
|
* spline2dbuildersetalgoblocklls() - BlockLLS algorithm, medium-scale problems
|
|
* spline2dbuildersetalgofastddm() - FastDDM algorithm, large-scale problems
|
|
* finally, fitting itself is performed with spline2dfit() function.
|
|
|
|
Most of the steps above can be omitted, solver is configured with good
|
|
defaults. The minimum is to call:
|
|
* spline2dbuildercreate() to create solver object
|
|
* spline2dbuildersetpoints() to specify dataset
|
|
* spline2dbuildersetgrid() to tell how many nodes you need
|
|
* spline2dfit() to perform fit
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
D - positive number, number of Y-components: D=1 for simple scalar
|
|
fit, D>1 for vector-valued spline fitting.
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - solver object
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.01.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildercreate(const ae_int_t d, spline2dbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildercreate(d, const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets constant prior term (model is a sum of bicubic spline
|
|
and global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
Constant prior term is determined by least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
V - value for user-defined prior
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetuserterm(const spline2dbuilder &state, const double v, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetuserterm(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), v, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets linear prior term (model is a sum of bicubic spline and
|
|
global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
Linear prior term is determined by least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetlinterm(const spline2dbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetlinterm(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets constant prior term (model is a sum of bicubic spline
|
|
and global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
Constant prior term is determined by least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetconstterm(const spline2dbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetconstterm(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets zero prior term (model is a sum of bicubic spline and
|
|
global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetzeroterm(const spline2dbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetzeroterm(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function adds dataset to the builder object.
|
|
|
|
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 - spline 2D builder object
|
|
XY - points, array[N,2+D]. One row corresponds to one point
|
|
in the dataset. First 2 elements are coordinates, next
|
|
D elements are function values. Array may be larger than
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetpoints(const spline2dbuilder &state, const real_2d_array &xy, const ae_int_t n, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetpoints(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets area where 2D spline interpolant is built. "Auto" means
|
|
that area extent is determined automatically from dataset extent.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetareaauto(const spline2dbuilder &state, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetareaauto(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets area where 2D spline interpolant is built to
|
|
user-defined one: [XA,XB]*[YA,YB]
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
XA,XB - spatial extent in the first (X) dimension, XA<XB
|
|
YA,YB - spatial extent in the second (Y) dimension, YA<YB
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetarea(const spline2dbuilder &state, const double xa, const double xb, const double ya, const double yb, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetarea(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), xa, xb, ya, yb, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets nodes count for 2D spline interpolant. Fitting is
|
|
performed on area defined with one of the "setarea" functions; this one
|
|
sets number of nodes placed upon the fitting area.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
KX - nodes count for the first (X) dimension; fitting interval
|
|
[XA,XB] is separated into KX-1 subintervals, with KX nodes
|
|
created at the boundaries.
|
|
KY - nodes count for the first (Y) dimension; fitting interval
|
|
[YA,YB] is separated into KY-1 subintervals, with KY nodes
|
|
created at the boundaries.
|
|
|
|
NOTE: at least 4 nodes is created in each dimension, so KX and KY are
|
|
silently increased if needed.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetgrid(const spline2dbuilder &state, const ae_int_t kx, const ae_int_t ky, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetgrid(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), kx, ky, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function allows you to choose least squares solver used to perform
|
|
fitting. This function sets solver algorithm to "FastDDM", which performs
|
|
fast parallel fitting by splitting problem into smaller chunks and merging
|
|
results together.
|
|
|
|
This solver is optimized for large-scale problems, starting from 256x256
|
|
grids, and up to 10000x10000 grids. Of course, it will work for smaller
|
|
grids too.
|
|
|
|
More detailed description of the algorithm is given below:
|
|
* algorithm generates hierarchy of nested grids, ranging from ~16x16
|
|
(topmost "layer" of the model) to ~KX*KY one (final layer). Upper layers
|
|
model global behavior of the function, lower layers are used to model
|
|
fine details. Moving from layer to layer doubles grid density.
|
|
* fitting is started from topmost layer, subsequent layers are fitted
|
|
using residuals from previous ones.
|
|
* user may choose to skip generation of upper layers and generate only a
|
|
few bottom ones, which will result in much better performance and
|
|
parallelization efficiency, at the cost of algorithm inability to "patch"
|
|
large holes in the dataset.
|
|
* every layer is regularized using progressively increasing regularization
|
|
coefficient; thus, increasing LambdaV penalizes fine details first,
|
|
leaving lower frequencies almost intact for a while.
|
|
* after fitting is done, all layers are merged together into one bicubic
|
|
spline
|
|
|
|
IMPORTANT: regularization coefficient used by this solver is different
|
|
from the one used by BlockLLS. Latter utilizes nonlinearity
|
|
penalty, which is global in nature (large regularization
|
|
results in global linear trend being extracted); this solver
|
|
uses another, localized form of penalty, which is suitable for
|
|
parallel processing.
|
|
|
|
Notes on memory and performance:
|
|
* memory requirements: most memory is consumed during modeling of the
|
|
higher layers; ~[512*NPoints] bytes is required for a model with full
|
|
hierarchy of grids being generated. However, if you skip a few topmost
|
|
layers, you will get nearly constant (wrt. points count and grid size)
|
|
memory consumption.
|
|
* serial running time: O(K*K)+O(NPoints) for a KxK grid
|
|
* parallelism potential: good. You may get nearly linear speed-up when
|
|
performing fitting with just a few layers. Adding more layers results in
|
|
model becoming more global, which somewhat reduces efficiency of the
|
|
parallel code.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
NLayers - number of layers in the model:
|
|
* NLayers>=1 means that up to chosen number of bottom
|
|
layers is fitted
|
|
* NLayers=0 means that maximum number of layers is chosen
|
|
(according to current grid size)
|
|
* NLayers<=-1 means that up to |NLayers| topmost layers is
|
|
skipped
|
|
Recommendations:
|
|
* good "default" value is 2 layers
|
|
* you may need more layers, if your dataset is very
|
|
irregular and you want to "patch" large holes. For a
|
|
grid step H (equal to AreaWidth/GridSize) you may expect
|
|
that last layer reproduces variations at distance H (and
|
|
can patch holes that wide); that higher layers operate
|
|
at distances 2*H, 4*H, 8*H and so on.
|
|
* good value for "bullletproof" mode is NLayers=0, which
|
|
results in complete hierarchy of layers being generated.
|
|
LambdaV - regularization coefficient, chosen in such a way that it
|
|
penalizes bottom layers (fine details) first.
|
|
LambdaV>=0, zero value means that no penalty is applied.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetalgofastddm(const spline2dbuilder &state, const ae_int_t nlayers, const double lambdav, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetalgofastddm(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), nlayers, lambdav, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function allows you to choose least squares solver used to perform
|
|
fitting. This function sets solver algorithm to "BlockLLS", which performs
|
|
least squares fitting with fast sparse direct solver, with optional
|
|
nonsmoothness penalty being applied.
|
|
|
|
Nonlinearity penalty has the following form:
|
|
|
|
[ ]
|
|
P() ~ Lambda* integral[ (d2S/dx2)^2 + 2*(d2S/dxdy)^2 + (d2S/dy2)^2 ]dxdy
|
|
[ ]
|
|
|
|
here integral is calculated over entire grid, and "~" means "proportional"
|
|
because integral is normalized after calcilation. Extremely large values
|
|
of Lambda result in linear fit being performed.
|
|
|
|
NOTE: this algorithm is the most robust and controllable one, but it is
|
|
limited by 512x512 grids and (say) up to 1.000.000 points. However,
|
|
ALGLIB has one more spline solver: FastDDM algorithm, which is
|
|
intended for really large-scale problems (in 10M-100M range). FastDDM
|
|
algorithm also has better parallelism properties.
|
|
|
|
More information on BlockLLS solver:
|
|
* memory requirements: ~[32*K^3+256*NPoints] bytes for KxK grid with
|
|
NPoints-sized dataset
|
|
* serial running time: O(K^4+NPoints)
|
|
* parallelism potential: limited. You may get some sublinear gain when
|
|
working with large grids (K's in 256..512 range)
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
LambdaNS- non-negative value:
|
|
* positive value means that some smoothing is applied
|
|
* zero value means that no smoothing is applied, and
|
|
corresponding entries of design matrix are numerically
|
|
zero and dropped from consideration.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetalgoblocklls(const spline2dbuilder &state, const double lambdans, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetalgoblocklls(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), lambdans, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function allows you to choose least squares solver used to perform
|
|
fitting. This function sets solver algorithm to "NaiveLLS".
|
|
|
|
IMPORTANT: NaiveLLS is NOT intended to be used in real life code! This
|
|
algorithm solves problem by generated dense (K^2)x(K^2+NPoints)
|
|
matrix and solves linear least squares problem with dense
|
|
solver.
|
|
|
|
It is here just to test BlockLLS against reference solver
|
|
(and maybe for someone trying to compare well optimized solver
|
|
against straightforward approach to the LLS problem).
|
|
|
|
More information on naive LLS solver:
|
|
* memory requirements: ~[8*K^4+256*NPoints] bytes for KxK grid.
|
|
* serial running time: O(K^6+NPoints) for KxK grid
|
|
* when compared with BlockLLS, NaiveLLS has ~K larger memory demand and
|
|
~K^2 larger running time.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
LambdaNS- nonsmoothness penalty
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetalgonaivells(const spline2dbuilder &state, const double lambdans, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dbuildersetalgonaivells(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), lambdans, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function fits bicubic spline to current dataset, using current area/
|
|
grid and current LLS solver.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
State - spline 2D builder object
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - 2D spline, fit result
|
|
Rep - fitting report, which provides some additional info about
|
|
errors, R2 coefficient and so on.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dfit(const spline2dbuilder &state, spline2dinterpolant &s, spline2dfitreport &rep, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::spline2dfit(const_cast<alglib_impl::spline2dbuilder*>(state.c_ptr()), const_cast<alglib_impl::spline2dinterpolant*>(s.c_ptr()), const_cast<alglib_impl::spline2dfitreport*>(rep.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RBFV1) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_RBF) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
Buffer object which is used to perform nearest neighbor requests in the
|
|
multithreaded mode (multiple threads working with same KD-tree object).
|
|
|
|
This object should be created with KDTreeCreateBuffer().
|
|
*************************************************************************/
|
|
_rbfcalcbuffer_owner::_rbfcalcbuffer_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfcalcbuffer_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::rbfcalcbuffer*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfcalcbuffer), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfcalcbuffer));
|
|
alglib_impl::_rbfcalcbuffer_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_rbfcalcbuffer_owner::_rbfcalcbuffer_owner(const _rbfcalcbuffer_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfcalcbuffer_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: rbfcalcbuffer copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::rbfcalcbuffer*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfcalcbuffer), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfcalcbuffer));
|
|
alglib_impl::_rbfcalcbuffer_init_copy(p_struct, const_cast<alglib_impl::rbfcalcbuffer*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_rbfcalcbuffer_owner& _rbfcalcbuffer_owner::operator=(const _rbfcalcbuffer_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: rbfcalcbuffer assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: rbfcalcbuffer assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_rbfcalcbuffer_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfcalcbuffer));
|
|
alglib_impl::_rbfcalcbuffer_init_copy(p_struct, const_cast<alglib_impl::rbfcalcbuffer*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_rbfcalcbuffer_owner::~_rbfcalcbuffer_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfcalcbuffer_destroy(p_struct);
|
|
ae_free(p_struct);
|
|
}
|
|
}
|
|
|
|
alglib_impl::rbfcalcbuffer* _rbfcalcbuffer_owner::c_ptr()
|
|
{
|
|
return p_struct;
|
|
}
|
|
|
|
alglib_impl::rbfcalcbuffer* _rbfcalcbuffer_owner::c_ptr() const
|
|
{
|
|
return const_cast<alglib_impl::rbfcalcbuffer*>(p_struct);
|
|
}
|
|
rbfcalcbuffer::rbfcalcbuffer() : _rbfcalcbuffer_owner()
|
|
{
|
|
}
|
|
|
|
rbfcalcbuffer::rbfcalcbuffer(const rbfcalcbuffer &rhs):_rbfcalcbuffer_owner(rhs)
|
|
{
|
|
}
|
|
|
|
rbfcalcbuffer& rbfcalcbuffer::operator=(const rbfcalcbuffer &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
_rbfcalcbuffer_owner::operator=(rhs);
|
|
return *this;
|
|
}
|
|
|
|
rbfcalcbuffer::~rbfcalcbuffer()
|
|
{
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
RBF model.
|
|
|
|
Never try to directly work with fields of this object - always use ALGLIB
|
|
functions to use this object.
|
|
*************************************************************************/
|
|
_rbfmodel_owner::_rbfmodel_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfmodel_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::rbfmodel*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfmodel), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfmodel));
|
|
alglib_impl::_rbfmodel_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_rbfmodel_owner::_rbfmodel_owner(const _rbfmodel_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfmodel_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: rbfmodel copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::rbfmodel*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfmodel), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfmodel));
|
|
alglib_impl::_rbfmodel_init_copy(p_struct, const_cast<alglib_impl::rbfmodel*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_rbfmodel_owner& _rbfmodel_owner::operator=(const _rbfmodel_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: rbfmodel assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: rbfmodel assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_rbfmodel_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfmodel));
|
|
alglib_impl::_rbfmodel_init_copy(p_struct, const_cast<alglib_impl::rbfmodel*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_rbfmodel_owner::~_rbfmodel_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfmodel_destroy(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.
|
|
|
|
Fields which are set by modern RBF solvers (hierarchical):
|
|
* RMSError - root-mean-square error; NAN for old solvers (ML, QNN)
|
|
* MaxError - maximum error; NAN for old solvers (ML, QNN)
|
|
*************************************************************************/
|
|
_rbfreport_owner::_rbfreport_owner()
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
p_struct = (alglib_impl::rbfreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfreport));
|
|
alglib_impl::_rbfreport_init(p_struct, &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_rbfreport_owner::_rbfreport_owner(const _rbfreport_owner &rhs)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfreport_destroy(p_struct);
|
|
alglib_impl::ae_free(p_struct);
|
|
}
|
|
p_struct = NULL;
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
p_struct = NULL;
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: rbfreport copy constructor failure (source is not initialized)", &_state);
|
|
p_struct = (alglib_impl::rbfreport*)alglib_impl::ae_malloc(sizeof(alglib_impl::rbfreport), &_state);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfreport));
|
|
alglib_impl::_rbfreport_init_copy(p_struct, const_cast<alglib_impl::rbfreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
}
|
|
|
|
_rbfreport_owner& _rbfreport_owner::operator=(const _rbfreport_owner &rhs)
|
|
{
|
|
if( this==&rhs )
|
|
return *this;
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _state;
|
|
|
|
alglib_impl::ae_state_init(&_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_state.error_msg);
|
|
return *this;
|
|
#endif
|
|
}
|
|
alglib_impl::ae_state_set_break_jump(&_state, &_break_jump);
|
|
alglib_impl::ae_assert(p_struct!=NULL, "ALGLIB: rbfreport assignment constructor failure (destination is not initialized)", &_state);
|
|
alglib_impl::ae_assert(rhs.p_struct!=NULL, "ALGLIB: rbfreport assignment constructor failure (source is not initialized)", &_state);
|
|
alglib_impl::_rbfreport_destroy(p_struct);
|
|
memset(p_struct, 0, sizeof(alglib_impl::rbfreport));
|
|
alglib_impl::_rbfreport_init_copy(p_struct, const_cast<alglib_impl::rbfreport*>(rhs.p_struct), &_state, ae_false);
|
|
ae_state_clear(&_state);
|
|
return *this;
|
|
}
|
|
|
|
_rbfreport_owner::~_rbfreport_owner()
|
|
{
|
|
if( p_struct!=NULL )
|
|
{
|
|
alglib_impl::_rbfreport_destroy(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() ,rmserror(p_struct->rmserror),maxerror(p_struct->maxerror),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) ,rmserror(p_struct->rmserror),maxerror(p_struct->maxerror),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)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
alglib_impl::ae_int_t ssize;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
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, &state);
|
|
alglib_impl::ae_assert( s_out.length()<=(size_t)ssize, "ALGLIB: serialization integrity error", &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
/*************************************************************************
|
|
This function unserializes data structure from string.
|
|
*************************************************************************/
|
|
void rbfunserialize(const std::string &s_in, rbfmodel &obj)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
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, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function serializes data structure to C++ stream.
|
|
|
|
Data stream generated by this function is same as string representation
|
|
generated by string version of serializer - alphanumeric characters,
|
|
dots, underscores, minus signs, which are grouped into words separated by
|
|
spaces and CR+LF.
|
|
|
|
We recommend you to read comments on string version of serializer to find
|
|
out more about serialization of AlGLIB objects.
|
|
*************************************************************************/
|
|
void rbfserialize(rbfmodel &obj, std::ostream &s_out)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_alloc_start(&serializer);
|
|
alglib_impl::rbfalloc(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_get_alloc_size(&serializer); // not actually needed, but we have to ask
|
|
alglib_impl::ae_serializer_sstart_stream(&serializer, &s_out);
|
|
alglib_impl::rbfserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
/*************************************************************************
|
|
This function unserializes data structure from stream.
|
|
*************************************************************************/
|
|
void rbfunserialize(const std::istream &s_in, rbfmodel &obj)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state state;
|
|
alglib_impl::ae_serializer serializer;
|
|
|
|
alglib_impl::ae_state_init(&state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&state, &_break_jump);
|
|
alglib_impl::ae_serializer_init(&serializer);
|
|
alglib_impl::ae_serializer_ustart_stream(&serializer, &s_in);
|
|
alglib_impl::rbfunserialize(&serializer, obj.c_ptr(), &state);
|
|
alglib_impl::ae_serializer_stop(&serializer, &state);
|
|
alglib_impl::ae_serializer_clear(&serializer);
|
|
alglib_impl::ae_state_clear(&state);
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function creates RBF model for a scalar (NY=1) or vector (NY>1)
|
|
function in a NX-dimensional space (NX>=1).
|
|
|
|
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) or rbfsetpointsandscales().
|
|
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 tweaks algorithm properties with rbfsetalgohierarchical() method
|
|
(or chooses one of the legacy algorithms - 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.
|
|
|
|
IMPORTANT: we recommend you to use latest model construction algorithm -
|
|
hierarchical RBFs, which is activated by rbfsetalgohierarchical()
|
|
function. This algorithm is the fastest one, and most memory-
|
|
efficient.
|
|
However, it is incompatible with older versions of ALGLIB
|
|
(pre-3.11). So, if you serialize hierarchical model, you will
|
|
be unable to load it in pre-3.11 ALGLIB. Other model types (QNN
|
|
and RBF-ML) are still backward-compatible.
|
|
|
|
INPUT PARAMETERS:
|
|
NX - dimension of the space, NX>=1
|
|
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. Some additional memory
|
|
is allocated during model construction, but most of this memory is
|
|
freed after model coefficients are calculated. Amount of this
|
|
additional memory depends on model construction algorithm being
|
|
used.
|
|
|
|
NOTE 2: prior to ALGLIB version 3.11, RBF models supported only NX=2 or
|
|
NX=3. Any attempt to create single-dimensional or more than
|
|
3-dimensional RBF model resulted in exception.
|
|
|
|
ALGLIB 3.11 supports any NX>0, but models created with NX!=2 and
|
|
NX!=3 are incompatible with (a) older versions of ALGLIB, (b) old
|
|
model construction algorithms (QNN or RBF-ML).
|
|
|
|
So, if you create a model with NX=2 or NX=3, then, depending on
|
|
specific model construction algorithm being chosen, you will (QNN
|
|
and RBF-ML) or will not (HierarchicalRBF) get backward compatibility
|
|
with older versions of ALGLIB. You have a choice here.
|
|
|
|
However, if you create a model with NX neither 2 nor 3, you have
|
|
no backward compatibility from the start, and you are forced to
|
|
use hierarchical RBFs and ALGLIB 3.11 or later.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011, 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcreate(const ae_int_t nx, const ae_int_t ny, rbfmodel &s, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function creates buffer structure which can be used to perform
|
|
parallel RBF model evaluations (with one RBF model instance being
|
|
used from multiple threads, as long as different threads use different
|
|
instances of buffer).
|
|
|
|
This buffer object can be used with rbftscalcbuf() function (here "ts"
|
|
stands for "thread-safe", "buf" is a suffix which denotes function which
|
|
reuses previously allocated output space).
|
|
|
|
How to use it:
|
|
* create RBF model structure with rbfcreate()
|
|
* load data, tune parameters
|
|
* call rbfbuildmodel()
|
|
* call rbfcreatecalcbuffer(), once per thread working with RBF model (you
|
|
should call this function only AFTER call to rbfbuildmodel(), see below
|
|
for more information)
|
|
* call rbftscalcbuf() from different threads, with each thread working
|
|
with its own copy of buffer object.
|
|
|
|
INPUT PARAMETERS
|
|
S - RBF model
|
|
|
|
OUTPUT PARAMETERS
|
|
Buf - external buffer.
|
|
|
|
|
|
IMPORTANT: buffer object should be used only with RBF model object which
|
|
was used to initialize buffer. Any attempt to use buffer with
|
|
different object is dangerous - you may get memory violation
|
|
error because sizes of internal arrays do not fit to dimensions
|
|
of RBF structure.
|
|
|
|
IMPORTANT: you should call this function only for model which was built
|
|
with rbfbuildmodel() function, after successful invocation of
|
|
rbfbuildmodel(). Sizes of some internal structures are
|
|
determined only after model is built, so buffer object created
|
|
before model construction stage will be useless (and any
|
|
attempt to use it will result in exception).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.04.2016 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
void rbfcreatecalcbuffer(const rbfmodel &s, rbfcalcbuffer &buf, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfcreatecalcbuffer(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::rbfcalcbuffer*>(buf.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
IMPORTANT: ALGLIB version 3.11 and later allows you to specify a set of
|
|
per-dimension scales. Interpolation radii are multiplied by the
|
|
scale vector. It may be useful if you have mixed spatio-temporal
|
|
data (say, a set of 3D slices recorded at different times).
|
|
You should call rbfsetpointsandscales() function to use this
|
|
feature.
|
|
|
|
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
|
|
specified, 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: dataset added by this function is not saved during model serialization.
|
|
MODEL ITSELF is serialized, but data used to build it are not.
|
|
|
|
So, if you 1) add dataset to empty RBF model, 2) serialize and
|
|
unserialize it, then you will get an empty RBF model with no dataset
|
|
being attached.
|
|
|
|
From the other side, if you call rbfbuildmodel() between (1) and (2),
|
|
then after (2) you will get your fully constructed RBF model - but
|
|
again with no dataset attached, so subsequent calls to rbfbuildmodel()
|
|
will produce empty model.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetpoints(const rbfmodel &s, const real_2d_array &xy, const ae_int_t n, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
IMPORTANT: ALGLIB version 3.11 and later allows you to specify a set of
|
|
per-dimension scales. Interpolation radii are multiplied by the
|
|
scale vector. It may be useful if you have mixed spatio-temporal
|
|
data (say, a set of 3D slices recorded at different times).
|
|
You should call rbfsetpointsandscales() function to use this
|
|
feature.
|
|
|
|
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
|
|
specified, 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: dataset added by this function is not saved during model serialization.
|
|
MODEL ITSELF is serialized, but data used to build it are not.
|
|
|
|
So, if you 1) add dataset to empty RBF model, 2) serialize and
|
|
unserialize it, then you will get an empty RBF model with no dataset
|
|
being attached.
|
|
|
|
From the other side, if you call rbfbuildmodel() between (1) and (2),
|
|
then after (2) you will get your fully constructed RBF model - but
|
|
again with no dataset attached, so subsequent calls to rbfbuildmodel()
|
|
will produce empty model.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void rbfsetpoints(const rbfmodel &s, const real_2d_array &xy, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = xy.rows();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
This function adds dataset and a vector of per-dimension scales.
|
|
|
|
It may be useful if you have mixed spatio-temporal data - say, a set of 3D
|
|
slices recorded at different times. Such data typically require different
|
|
RBF radii for spatial and temporal dimensions. ALGLIB solves this problem
|
|
by specifying single RBF radius, which is (optionally) multiplied by the
|
|
scale vector.
|
|
|
|
This function overrides results of the previous calls, i.e. multiple calls
|
|
of this function will result in only the last set being added.
|
|
|
|
IMPORTANT: only HierarchicalRBF algorithm can work with scaled points. So,
|
|
using this function results in RBF models which can be used in
|
|
ALGLIB 3.11 or later. Previous versions of the library will be
|
|
unable to unserialize models produced by HierarchicalRBF algo.
|
|
|
|
Any attempt to use this function with RBF-ML or QNN algorithms
|
|
will result in -3 error code being returned (incorrect
|
|
algorithm).
|
|
|
|
INPUT PARAMETERS:
|
|
R - 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
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
S - array[NX], scale vector, S[i]>0.
|
|
|
|
After you've added dataset and (optionally) tuned algorithm settings you
|
|
should call rbfbuildmodel() in order to build a model for you.
|
|
|
|
NOTE: dataset added by this function is not saved during model serialization.
|
|
MODEL ITSELF is serialized, but data used to build it are not.
|
|
|
|
So, if you 1) add dataset to empty RBF model, 2) serialize and
|
|
unserialize it, then you will get an empty RBF model with no dataset
|
|
being attached.
|
|
|
|
From the other side, if you call rbfbuildmodel() between (1) and (2),
|
|
then after (2) you will get your fully constructed RBF model - but
|
|
again with no dataset attached, so subsequent calls to rbfbuildmodel()
|
|
will produce empty model.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetpointsandscales(const rbfmodel &r, const real_2d_array &xy, const ae_int_t n, const real_1d_array &s, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetpointsandscales(const_cast<alglib_impl::rbfmodel*>(r.c_ptr()), const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function adds dataset and a vector of per-dimension scales.
|
|
|
|
It may be useful if you have mixed spatio-temporal data - say, a set of 3D
|
|
slices recorded at different times. Such data typically require different
|
|
RBF radii for spatial and temporal dimensions. ALGLIB solves this problem
|
|
by specifying single RBF radius, which is (optionally) multiplied by the
|
|
scale vector.
|
|
|
|
This function overrides results of the previous calls, i.e. multiple calls
|
|
of this function will result in only the last set being added.
|
|
|
|
IMPORTANT: only HierarchicalRBF algorithm can work with scaled points. So,
|
|
using this function results in RBF models which can be used in
|
|
ALGLIB 3.11 or later. Previous versions of the library will be
|
|
unable to unserialize models produced by HierarchicalRBF algo.
|
|
|
|
Any attempt to use this function with RBF-ML or QNN algorithms
|
|
will result in -3 error code being returned (incorrect
|
|
algorithm).
|
|
|
|
INPUT PARAMETERS:
|
|
R - 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
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
S - array[NX], scale vector, S[i]>0.
|
|
|
|
After you've added dataset and (optionally) tuned algorithm settings you
|
|
should call rbfbuildmodel() in order to build a model for you.
|
|
|
|
NOTE: dataset added by this function is not saved during model serialization.
|
|
MODEL ITSELF is serialized, but data used to build it are not.
|
|
|
|
So, if you 1) add dataset to empty RBF model, 2) serialize and
|
|
unserialize it, then you will get an empty RBF model with no dataset
|
|
being attached.
|
|
|
|
From the other side, if you call rbfbuildmodel() between (1) and (2),
|
|
then after (2) you will get your fully constructed RBF model - but
|
|
again with no dataset attached, so subsequent calls to rbfbuildmodel()
|
|
will produce empty model.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void rbfsetpointsandscales(const rbfmodel &r, const real_2d_array &xy, const real_1d_array &s, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
|
|
n = xy.rows();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetpointsandscales(const_cast<alglib_impl::rbfmodel*>(r.c_ptr()), const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), n, const_cast<alglib_impl::ae_vector*>(s.c_ptr()), &_alglib_env_state);
|
|
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
DEPRECATED:since version 3.11 ALGLIB includes new RBF model construction
|
|
algorithm, Hierarchical RBF. This algorithm is faster and
|
|
requires less memory than QNN and RBF-ML. It is especially good
|
|
for large-scale interpolation problems. So, we recommend you to
|
|
consider Hierarchical RBF as default option.
|
|
|
|
==========================================================================
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
DEPRECATED:since version 3.11 ALGLIB includes new RBF model construction
|
|
algorithm, Hierarchical RBF. This algorithm is faster and
|
|
requires less memory than QNN and RBF-ML. It is especially good
|
|
for large-scale interpolation problems. So, we recommend you to
|
|
consider Hierarchical RBF as default option.
|
|
|
|
==========================================================================
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void rbfsetalgoqnn(const rbfmodel &s, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
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);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
DEPRECATED:since version 3.11 ALGLIB includes new RBF model construction
|
|
algorithm, Hierarchical RBF. This algorithm is faster and
|
|
requires less memory than QNN and RBF-ML. It is especially good
|
|
for large-scale interpolation problems. So, we recommend you to
|
|
consider Hierarchical RBF as default option.
|
|
|
|
==========================================================================
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
DEPRECATED:since version 3.11 ALGLIB includes new RBF model construction
|
|
algorithm, Hierarchical RBF. This algorithm is faster and
|
|
requires less memory than QNN and RBF-ML. It is especially good
|
|
for large-scale interpolation problems. So, we recommend you to
|
|
consider Hierarchical RBF as default option.
|
|
|
|
==========================================================================
|
|
|
|
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
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
void rbfsetalgomultilayer(const rbfmodel &s, const double rbase, const ae_int_t nlayers, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
double lambdav;
|
|
|
|
lambdav = 0.01;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called Hierarchical RBF. It similar to its previous
|
|
incarnation, RBF-ML, i.e. it also builds a sequence of models with
|
|
decreasing radii. However, it uses more economical way of building upper
|
|
layers (ones with large radii), which results in faster model construction
|
|
and evaluation, as well as smaller memory footprint during construction.
|
|
|
|
This algorithm has following important features:
|
|
* ability to handle millions of points
|
|
* controllable smoothing via nonlinearity penalization
|
|
* support for NX-dimensional models with NX=1 or NX>3 (unlike QNN or RBF-ML)
|
|
* support for specification of per-dimensional radii via scale vector,
|
|
which is set by means of rbfsetpointsandscales() function. This feature
|
|
is useful if you solve spatio-temporal interpolation problems, where
|
|
different radii are required for spatial and temporal dimensions.
|
|
|
|
Running times are roughly proportional to:
|
|
* N*log(N)*NLayers - for model construction
|
|
* N*NLayers - for model evaluation
|
|
You may see that running time does not depend on search radius or points
|
|
density, just on number of layers in the hierarchy.
|
|
|
|
IMPORTANT: this model construction algorithm was introduced in ALGLIB 3.11
|
|
and produces models which are INCOMPATIBLE with previous
|
|
versions of ALGLIB. You can not unserialize models produced
|
|
with this function in ALGLIB 3.10 or earlier.
|
|
|
|
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.
|
|
LambdaNS- >=0, nonlinearity penalty coefficient, negative values are
|
|
not allowed. This parameter adds controllable smoothing to
|
|
the problem, which may reduce noise. Specification of non-
|
|
zero lambda means that in addition to fitting error solver
|
|
will also minimize LambdaNS*|S''(x)|^2 (appropriately
|
|
generalized to multiple dimensions.
|
|
|
|
Specification of exactly zero value means that no penalty
|
|
is added (we do not even evaluate matrix of second
|
|
derivatives which is necessary for smoothing).
|
|
|
|
Calculation of nonlinearity penalty is costly - it results
|
|
in several-fold increase of model construction time.
|
|
Evaluation time remains the same.
|
|
|
|
Optimal lambda is problem-dependent and requires trial
|
|
and error. Good value to start from is 1e-5...1e-6,
|
|
which corresponds to slightly noticeable smoothing of the
|
|
function. Value 1e-2 usually means that quite heavy
|
|
smoothing is applied.
|
|
|
|
TUNING ALGORITHM
|
|
|
|
In order to use this algorithm you have to choose three parameters:
|
|
* initial radius RBase
|
|
* number of layers in the model NLayers
|
|
* penalty coefficient LambdaNS
|
|
|
|
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 (or specify
|
|
non-zero LambdaNS).
|
|
|
|
TYPICAL ERRORS
|
|
|
|
1. 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.
|
|
|
|
2. 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.
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgohierarchical(const rbfmodel &s, const double rbase, const ae_int_t nlayers, const double lambdans, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetalgohierarchical(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), rbase, nlayers, lambdans, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetlinterm(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetconstterm(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetzeroterm(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets basis function type, which can be:
|
|
* 0 for classic Gaussian
|
|
* 1 for fast and compact bell-like basis function, which becomes exactly
|
|
zero at distance equal to 3*R (default option).
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
BF - basis function type:
|
|
* 0 - classic Gaussian
|
|
* 1 - fast and compact one
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetv2bf(const rbfmodel &s, const ae_int_t bf, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetv2bf(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), bf, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets stopping criteria of the underlying linear solver for
|
|
hierarchical (version 2) RBF constructor.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
MaxIts - this criterion will stop algorithm after MaxIts iterations.
|
|
Typically a few hundreds iterations is required, with 400
|
|
being a good default value to start experimentation.
|
|
Zero value means that default value will be selected.
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetv2its(const rbfmodel &s, const ae_int_t maxits, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetv2its(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), maxits, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function sets support radius parameter of hierarchical (version 2)
|
|
RBF constructor.
|
|
|
|
Hierarchical RBF model achieves great speed-up by removing from the model
|
|
excessive (too dense) nodes. Say, if you have RBF radius equal to 1 meter,
|
|
and two nodes are just 1 millimeter apart, you may remove one of them
|
|
without reducing model quality.
|
|
|
|
Support radius parameter is used to justify which points need removal, and
|
|
which do not. If two points are less than SUPPORT_R*CUR_RADIUS units of
|
|
distance apart, one of them is removed from the model. The larger support
|
|
radius is, the faster model construction AND evaluation are. However,
|
|
too large values result in "bumpy" models.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
R - support radius coefficient, >=0.
|
|
Recommended values are [0.1,0.4] range, with 0.1 being
|
|
default value.
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetv2supportr(const rbfmodel &s, const double r, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfsetv2supportr(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), r, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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; only QNN returns this
|
|
error code, other algorithms can handle non-
|
|
distinct nodes.
|
|
* -4 - nonconvergence of the internal SVD solver
|
|
* -3 incorrect model construction algorithm was chosen:
|
|
QNN or RBF-ML, combined with one of the incompatible
|
|
features - NX=1 or NX>3; points with per-dimension
|
|
scales.
|
|
* 1 - successful termination
|
|
* 8 - a termination request was submitted via
|
|
rbfrequesttermination() function.
|
|
|
|
Fields which are set only by modern RBF solvers (hierarchical
|
|
or nonnegative; older solvers like QNN and ML initialize these
|
|
fields by NANs):
|
|
* rep.rmserror - root-mean-square error at nodes
|
|
* rep.maxerror - maximum error at nodes
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model in the given point.
|
|
|
|
IMPORTANT: this function works only with modern (hierarchical) RBFs. It
|
|
can not be used with legacy (version 1) RBFs because older RBF
|
|
code does not support 1-dimensional models.
|
|
|
|
This function should be used when we have NY=1 (scalar function) and NX=1
|
|
(1-dimensional space). If you have 3-dimensional space, use rbfcalc3(). If
|
|
you have 2-dimensional space, use rbfcalc3(). If you have general
|
|
situation (NX-dimensional space, NY-dimensional function) you should use
|
|
generic rbfcalc().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>1
|
|
* NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - X-coordinate, finite number
|
|
|
|
RESULT:
|
|
value of the model or 0.0 (as defined above)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfcalc1(const rbfmodel &s, const double x0, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::rbfcalc1(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), x0, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&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=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 generic rbfcalc().
|
|
|
|
If you want to calculate function values many times, consider using
|
|
rbfgridcalc2v(), which is far more efficient than many subsequent calls to
|
|
rbfcalc2().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates value 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 generic rbfcalc().
|
|
|
|
If you want to calculate function values many times, consider using
|
|
rbfgridcalc3v(), which is far more efficient than many subsequent calls to
|
|
rbfcalc3().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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));
|
|
}
|
|
|
|
/*************************************************************************
|
|
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().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the given point, using
|
|
external buffer object (internal temporaries of RBF model are not
|
|
modified).
|
|
|
|
This function allows to use same RBF model object in different threads,
|
|
assuming that different threads use different instances of buffer
|
|
structure.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, may be shared between different threads
|
|
Buf - buffer object created for this particular instance of RBF
|
|
model with rbfcreatecalcbuffer().
|
|
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 rbftscalcbuf(const rbfmodel &s, const rbfcalcbuffer &buf, const real_1d_array &x, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbftscalcbuf(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), const_cast<alglib_impl::rbfcalcbuffer*>(buf.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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This is legacy function for gridded calculation of RBF model.
|
|
|
|
It is superseded by rbfgridcalc2v() and rbfgridcalc2vsubset() functions.
|
|
|
|
-- 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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the regular grid,
|
|
which has N0*N1 points, with Point[I,J] = (X0[I], X1[J]). Vector-valued
|
|
RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1], where NY is a number of
|
|
"output" vector values (this function supports vector-
|
|
valued RBF models). Y is out-variable and is reallocated
|
|
by this function.
|
|
Y[K+NY*(I0+I1*N0)]=F_k(X0[I0],X1[I1]), for:
|
|
* K=0...NY-1
|
|
* I0=0...N0-1
|
|
* I1=0...N1-1
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
NOTE: if you need function values on some subset of regular grid, which
|
|
may be described as "several compact and dense islands", you may
|
|
use rbfgridcalc2vsubset().
|
|
|
|
-- ALGLIB --
|
|
Copyright 27.01.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc2v(const rbfmodel &s, const real_1d_array &x0, const ae_int_t n0, const real_1d_array &x1, const ae_int_t n1, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfgridcalc2v(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_vector*>(y.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at some subset of regular
|
|
grid:
|
|
* grid has N0*N1 points, with Point[I,J] = (X0[I], X1[J])
|
|
* only values at some subset of this grid are required
|
|
Vector-valued RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
FlagY - array[N0*N1]:
|
|
* Y[I0+I1*N0] corresponds to node (X0[I0],X1[I1])
|
|
* it is a "bitmap" array which contains False for nodes
|
|
which are NOT calculated, and True for nodes which are
|
|
required.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1*N2], where NY is a number
|
|
of "output" vector values (this function supports vector-
|
|
valued RBF models):
|
|
* Y[K+NY*(I0+I1*N0)]=F_k(X0[I0],X1[I1]),
|
|
for K=0...NY-1, I0=0...N0-1, I1=0...N1-1.
|
|
* elements of Y[] which correspond to FlagY[]=True are
|
|
loaded by model values (which may be exactly zero for
|
|
some nodes).
|
|
* elements of Y[] which correspond to FlagY[]=False MAY be
|
|
initialized by zeros OR may be calculated. This function
|
|
processes grid as a hierarchy of nested blocks and
|
|
micro-rows. If just one element of micro-row is required,
|
|
entire micro-row (up to 8 nodes in the current version,
|
|
but no promises) is calculated.
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc2vsubset(const rbfmodel &s, const real_1d_array &x0, const ae_int_t n0, const real_1d_array &x1, const ae_int_t n1, const boolean_1d_array &flagy, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfgridcalc2vsubset(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_vector*>(flagy.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the regular grid,
|
|
which has N0*N1*N2 points, with Point[I,J,K] = (X0[I], X1[J], X2[K]).
|
|
Vector-valued RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>3
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
X2 - array of grid nodes, third coordinates, array[N2]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N2 - grid size (number of nodes) in the third dimension
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1*N2], where NY is a number
|
|
of "output" vector values (this function supports vector-
|
|
valued RBF models). Y is out-variable and is reallocated
|
|
by this function.
|
|
Y[K+NY*(I0+I1*N0+I2*N0*N1)]=F_k(X0[I0],X1[I1],X2[I2]), for:
|
|
* K=0...NY-1
|
|
* I0=0...N0-1
|
|
* I1=0...N1-1
|
|
* I2=0...N2-1
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
NOTE: if you need function values on some subset of regular grid, which
|
|
may be described as "several compact and dense islands", you may
|
|
use rbfgridcalc3vsubset().
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc3v(const rbfmodel &s, const real_1d_array &x0, const ae_int_t n0, const real_1d_array &x1, const ae_int_t n1, const real_1d_array &x2, const ae_int_t n2, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfgridcalc3v(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_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(y.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at some subset of regular
|
|
grid:
|
|
* grid has N0*N1*N2 points, with Point[I,J,K] = (X0[I], X1[J], X2[K])
|
|
* only values at some subset of this grid are required
|
|
Vector-valued RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>3
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
X2 - array of grid nodes, third coordinates, array[N2]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N2 - grid size (number of nodes) in the third dimension
|
|
|
|
FlagY - array[N0*N1*N2]:
|
|
* Y[I0+I1*N0+I2*N0*N1] corresponds to node (X0[I0],X1[I1],X2[I2])
|
|
* it is a "bitmap" array which contains False for nodes
|
|
which are NOT calculated, and True for nodes which are
|
|
required.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1*N2], where NY is a number
|
|
of "output" vector values (this function supports vector-
|
|
valued RBF models):
|
|
* Y[K+NY*(I0+I1*N0+I2*N0*N1)]=F_k(X0[I0],X1[I1],X2[I2]),
|
|
for K=0...NY-1, I0=0...N0-1, I1=0...N1-1, I2=0...N2-1.
|
|
* elements of Y[] which correspond to FlagY[]=True are
|
|
loaded by model values (which may be exactly zero for
|
|
some nodes).
|
|
* elements of Y[] which correspond to FlagY[]=False MAY be
|
|
initialized by zeros OR may be calculated. This function
|
|
processes grid as a hierarchy of nested blocks and
|
|
micro-rows. If just one element of micro-row is required,
|
|
entire micro-row (up to 8 nodes in the current version,
|
|
but no promises) is calculated.
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc3vsubset(const rbfmodel &s, const real_1d_array &x0, const ae_int_t n0, const real_1d_array &x1, const ae_int_t n1, const real_1d_array &x2, const ae_int_t n2, const boolean_1d_array &flagy, real_1d_array &y, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfgridcalc3vsubset(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_vector*>(x2.c_ptr()), n2, const_cast<alglib_impl::ae_vector*>(flagy.c_ptr()), const_cast<alglib_impl::ae_vector*>(y.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
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
|
|
For ModelVersion=1:
|
|
* last column - radius, same for all dimensions of
|
|
the function being modelled
|
|
For ModelVersion=2:
|
|
* last NX columns - radii, one per dimension
|
|
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.
|
|
ModelVersion-version of the RBF model:
|
|
* 1 - for models created by QNN and RBF-ML algorithms,
|
|
compatible with ALGLIB 3.10 or earlier.
|
|
* 2 - for models created by HierarchicalRBF, requires
|
|
ALGLIB 3.11 or later
|
|
|
|
-- 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, ae_int_t &modelversion, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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()), &modelversion, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function returns model version.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
|
|
RESULT:
|
|
* 1 - for models created by QNN and RBF-ML algorithms,
|
|
compatible with ALGLIB 3.10 or earlier.
|
|
* 2 - for models created by HierarchicalRBF, requires
|
|
ALGLIB 3.11 or later
|
|
|
|
-- ALGLIB --
|
|
Copyright 06.07.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
ae_int_t rbfgetmodelversion(const rbfmodel &s, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::ae_int_t result = alglib_impl::rbfgetmodelversion(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<ae_int_t*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is used to peek into hierarchical RBF construction process
|
|
from some other thread and get current progress indicator. It returns
|
|
value in [0,1].
|
|
|
|
IMPORTANT: only HRBFs (hierarchical RBFs) support peeking into progress
|
|
indicator. Legacy RBF-ML and RBF-QNN do not support it. You
|
|
will always get 0 value.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model object
|
|
|
|
RESULT:
|
|
progress value, in [0,1]
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.11.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfpeekprogress(const rbfmodel &s, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return 0;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
double result = alglib_impl::rbfpeekprogress(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return *(reinterpret_cast<double*>(&result));
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is used to submit a request for termination of the
|
|
hierarchical RBF construction process from some other thread. As result,
|
|
RBF construction is terminated smoothly (with proper deallocation of all
|
|
necessary resources) and resultant model is filled by zeros.
|
|
|
|
A rep.terminationtype=8 will be returned upon receiving such request.
|
|
|
|
IMPORTANT: only HRBFs (hierarchical RBFs) support termination requests.
|
|
Legacy RBF-ML and RBF-QNN do not support it. An attempt to
|
|
terminate their construction will be ignored.
|
|
|
|
IMPORTANT: termination request flag is cleared when the model construction
|
|
starts. Thus, any pre-construction termination requests will be
|
|
silently ignored - only ones submitted AFTER construction has
|
|
actually began will be handled.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model object
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.11.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfrequesttermination(const rbfmodel &s, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::rbfrequesttermination(const_cast<alglib_impl::rbfmodel*>(s.c_ptr()), &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_INTCOMP) || !defined(AE_PARTIAL_BUILD)
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspheremc() instead.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspheremcc(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, real_1d_array &cx, double &rhi, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::nsfitspheremcc(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rhi, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspheremi() instead.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspheremic(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, real_1d_array &cx, double &rlo, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::nsfitspheremic(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rlo, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspheremz() instead.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspheremzc(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, real_1d_array &cx, double &rlo, double &rhi, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::nsfitspheremzc(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rlo, &rhi, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspherex() instead.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspherex(const real_2d_array &xy, const ae_int_t npoints, const ae_int_t nx, const ae_int_t problemtype, const double epsx, const ae_int_t aulits, const double penalty, real_1d_array &cx, double &rlo, double &rhi, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
alglib_impl::nsfitspherex(const_cast<alglib_impl::ae_matrix*>(xy.c_ptr()), npoints, nx, problemtype, epsx, aulits, penalty, const_cast<alglib_impl::ae_vector*>(cx.c_ptr()), &rlo, &rhi, &_alglib_env_state);
|
|
alglib_impl::ae_state_clear(&_alglib_env_state);
|
|
return;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is an obsolete and deprecated version of fitting by
|
|
penalized cubic spline.
|
|
|
|
It was superseded by spline1dfit(), which is an orders of magnitude faster
|
|
and more memory-efficient implementation.
|
|
|
|
Do NOT use this function in the new code!
|
|
|
|
-- 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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is an obsolete and deprecated version of fitting by
|
|
penalized cubic spline.
|
|
|
|
It was superseded by spline1dfit(), which is an orders of magnitude faster
|
|
and more memory-efficient implementation.
|
|
|
|
Do NOT use this function in the new code!
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 18.08.2009 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dfitpenalized': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
/*************************************************************************
|
|
This function is an obsolete and deprecated version of fitting by
|
|
penalized cubic spline.
|
|
|
|
It was superseded by spline1dfit(), which is an orders of magnitude faster
|
|
and more memory-efficient implementation.
|
|
|
|
Do NOT use this function in the new code!
|
|
|
|
-- 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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
{
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
#else
|
|
_ALGLIB_SET_ERROR_FLAG(_alglib_env_state.error_msg);
|
|
return;
|
|
#endif
|
|
}
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
|
|
/*************************************************************************
|
|
This function is an obsolete and deprecated version of fitting by
|
|
penalized cubic spline.
|
|
|
|
It was superseded by spline1dfit(), which is an orders of magnitude faster
|
|
and more memory-efficient implementation.
|
|
|
|
Do NOT use this function in the new code!
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 19.10.2010 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
#if !defined(AE_NO_EXCEPTIONS)
|
|
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, const xparams _xparams)
|
|
{
|
|
jmp_buf _break_jump;
|
|
alglib_impl::ae_state _alglib_env_state;
|
|
ae_int_t n;
|
|
if( (x.length()!=y.length()) || (x.length()!=w.length()))
|
|
_ALGLIB_CPP_EXCEPTION("Error while calling 'spline1dfitpenalizedw': looks like one of arguments has wrong size");
|
|
n = x.length();
|
|
alglib_impl::ae_state_init(&_alglib_env_state);
|
|
if( setjmp(_break_jump) )
|
|
_ALGLIB_CPP_EXCEPTION(_alglib_env_state.error_msg);
|
|
ae_state_set_break_jump(&_alglib_env_state, &_break_jump);
|
|
if( _xparams.flags!=0x0 )
|
|
ae_state_set_flags(&_alglib_env_state, _xparams.flags);
|
|
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;
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// THIS SECTION CONTAINS IMPLEMENTATION OF COMPUTATIONAL CORE
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
namespace alglib_impl
|
|
{
|
|
#if defined(AE_COMPILE_IDW) || !defined(AE_PARTIAL_BUILD)
|
|
static double idw_w0 = 1.0;
|
|
static double idw_meps = 1.0E-50;
|
|
static ae_int_t idw_defaultnlayers = 16;
|
|
static double idw_defaultlambda0 = 0.3333;
|
|
static void idw_errormetricsviacalc(idwbuilder* state,
|
|
idwmodel* model,
|
|
idwreport* rep,
|
|
ae_state *_state);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RATINT) || !defined(AE_PARTIAL_BUILD)
|
|
static void ratint_barycentricnormalize(barycentricinterpolant* b,
|
|
ae_state *_state);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_FITSPHERE) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_INTFITSERV) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_SPLINE1D) || !defined(AE_PARTIAL_BUILD)
|
|
static double spline1d_lambdareg = 1.0e-9;
|
|
static double spline1d_cholreg = 1.0e-12;
|
|
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);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_PARAMETRIC) || !defined(AE_PARTIAL_BUILD)
|
|
static void parametric_pspline2par(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t pt,
|
|
/* Real */ ae_vector* p,
|
|
ae_state *_state);
|
|
static void parametric_pspline3par(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t pt,
|
|
/* Real */ ae_vector* p,
|
|
ae_state *_state);
|
|
static void parametric_rdpanalyzesectionpar(/* Real */ ae_matrix* xy,
|
|
ae_int_t i0,
|
|
ae_int_t i1,
|
|
ae_int_t d,
|
|
ae_int_t* worstidx,
|
|
double* worsterror,
|
|
ae_state *_state);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_SPLINE3D) || !defined(AE_PARTIAL_BUILD)
|
|
static void spline3d_spline3ddiff(spline3dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
double z,
|
|
double* f,
|
|
double* fx,
|
|
double* fy,
|
|
double* fxy,
|
|
ae_state *_state);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_POLINT) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_LSFIT) || !defined(AE_PARTIAL_BUILD)
|
|
static void lsfit_rdpanalyzesection(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t i0,
|
|
ae_int_t i1,
|
|
ae_int_t* worstidx,
|
|
double* worsterror,
|
|
ae_state *_state);
|
|
static void lsfit_rdprecursive(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t i0,
|
|
ae_int_t i1,
|
|
double eps,
|
|
/* Real */ ae_vector* xout,
|
|
/* Real */ ae_vector* yout,
|
|
ae_int_t* nout,
|
|
ae_state *_state);
|
|
static void lsfit_logisticfitinternal(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_bool is4pl,
|
|
double lambdav,
|
|
minlmstate* state,
|
|
minlmreport* replm,
|
|
/* Real */ ae_vector* p1,
|
|
double* flast,
|
|
ae_state *_state);
|
|
static void lsfit_logisticfit45errors(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double a,
|
|
double b,
|
|
double c,
|
|
double d,
|
|
double g,
|
|
lsfitreport* rep,
|
|
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);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RBFV2) || !defined(AE_PARTIAL_BUILD)
|
|
static double rbfv2_defaultlambdareg = 1.0E-6;
|
|
static double rbfv2_defaultsupportr = 0.10;
|
|
static ae_int_t rbfv2_defaultmaxits = 400;
|
|
static ae_int_t rbfv2_defaultbf = 1;
|
|
static ae_int_t rbfv2_maxnodesize = 6;
|
|
static double rbfv2_complexitymultiplier = 100.0;
|
|
static ae_bool rbfv2_rbfv2buildlinearmodel(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t modeltype,
|
|
/* Real */ ae_matrix* v,
|
|
ae_state *_state);
|
|
static void rbfv2_allocatecalcbuffer(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
ae_state *_state);
|
|
static void rbfv2_convertandappendtree(kdtree* curtree,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
ae_state *_state);
|
|
static void rbfv2_converttreerec(kdtree* curtree,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t nodeoffset,
|
|
ae_int_t nodesbase,
|
|
ae_int_t splitsbase,
|
|
ae_int_t cwbase,
|
|
/* Integer */ ae_vector* localnodes,
|
|
ae_int_t* localnodessize,
|
|
/* Real */ ae_vector* localsplits,
|
|
ae_int_t* localsplitssize,
|
|
/* Real */ ae_vector* localcw,
|
|
ae_int_t* localcwsize,
|
|
/* Real */ ae_matrix* xybuf,
|
|
ae_state *_state);
|
|
static void rbfv2_partialcalcrec(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double invr2,
|
|
double queryr2,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state);
|
|
static void rbfv2_partialrowcalcrec(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double invr2,
|
|
double rquery2,
|
|
double rfar2,
|
|
/* Real */ ae_vector* cx,
|
|
/* Real */ ae_vector* rx,
|
|
/* Boolean */ ae_vector* rf,
|
|
ae_int_t rowsize,
|
|
/* Real */ ae_vector* ry,
|
|
ae_state *_state);
|
|
static void rbfv2_preparepartialquery(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* kdboxmin,
|
|
/* Real */ ae_vector* kdboxmax,
|
|
ae_int_t nx,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t* cnt,
|
|
ae_state *_state);
|
|
static void rbfv2_partialqueryrec(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double queryr2,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* r2,
|
|
/* Integer */ ae_vector* offs,
|
|
ae_int_t* k,
|
|
ae_state *_state);
|
|
static ae_int_t rbfv2_partialcountrec(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double queryr2,
|
|
/* Real */ ae_vector* x,
|
|
ae_state *_state);
|
|
static void rbfv2_partialunpackrec(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
/* Real */ ae_vector* s,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t rootidx,
|
|
double r,
|
|
/* Real */ ae_matrix* xwr,
|
|
ae_int_t* k,
|
|
ae_state *_state);
|
|
static ae_int_t rbfv2_designmatrixrowsize(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
/* Real */ ae_vector* ri,
|
|
/* Integer */ ae_vector* kdroots,
|
|
/* Real */ ae_vector* kdboxmin,
|
|
/* Real */ ae_vector* kdboxmax,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t nh,
|
|
ae_int_t level,
|
|
double rcoeff,
|
|
/* Real */ ae_vector* x0,
|
|
rbfv2calcbuffer* calcbuf,
|
|
ae_state *_state);
|
|
static void rbfv2_designmatrixgeneraterow(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
/* Real */ ae_vector* ri,
|
|
/* Integer */ ae_vector* kdroots,
|
|
/* Real */ ae_vector* kdboxmin,
|
|
/* Real */ ae_vector* kdboxmax,
|
|
/* Integer */ ae_vector* cwrange,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t nh,
|
|
ae_int_t level,
|
|
ae_int_t bf,
|
|
double rcoeff,
|
|
ae_int_t rowsperpoint,
|
|
double penalty,
|
|
/* Real */ ae_vector* x0,
|
|
rbfv2calcbuffer* calcbuf,
|
|
/* Real */ ae_vector* tmpr2,
|
|
/* Integer */ ae_vector* tmpoffs,
|
|
/* Integer */ ae_vector* rowidx,
|
|
/* Real */ ae_vector* rowval,
|
|
ae_int_t* rowsize,
|
|
ae_state *_state);
|
|
static void rbfv2_zerofill(rbfv2model* s,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t bf,
|
|
ae_state *_state);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_SPLINE2D) || !defined(AE_PARTIAL_BUILD)
|
|
static double spline2d_cholreg = 1.0E-12;
|
|
static double spline2d_lambdaregblocklls = 1.0E-6;
|
|
static double spline2d_lambdaregfastddm = 1.0E-4;
|
|
static double spline2d_lambdadecay = 0.5;
|
|
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 spline2d_generatedesignmatrix(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
double smoothing,
|
|
double lambdareg,
|
|
spline1dinterpolant* basis1,
|
|
sparsematrix* av,
|
|
sparsematrix* ah,
|
|
ae_int_t* arows,
|
|
ae_state *_state);
|
|
static void spline2d_updatesplinetable(/* Real */ ae_vector* z,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t d,
|
|
spline1dinterpolant* basis1,
|
|
ae_int_t bfrad,
|
|
/* Real */ ae_vector* ftbl,
|
|
ae_int_t m,
|
|
ae_int_t n,
|
|
ae_int_t scalexy,
|
|
ae_state *_state);
|
|
static void spline2d_fastddmfit(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t basecasex,
|
|
ae_int_t basecasey,
|
|
ae_int_t maxcoresize,
|
|
ae_int_t interfacesize,
|
|
ae_int_t nlayers,
|
|
double smoothing,
|
|
ae_int_t lsqrcnt,
|
|
spline1dinterpolant* basis1,
|
|
spline2dinterpolant* spline,
|
|
spline2dfitreport* rep,
|
|
double tss,
|
|
ae_state *_state);
|
|
static void spline2d_fastddmfitlayer(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t basecasex,
|
|
ae_int_t tilex0,
|
|
ae_int_t tilex1,
|
|
ae_int_t tilescountx,
|
|
ae_int_t basecasey,
|
|
ae_int_t tiley0,
|
|
ae_int_t tiley1,
|
|
ae_int_t tilescounty,
|
|
ae_int_t maxcoresize,
|
|
ae_int_t interfacesize,
|
|
ae_int_t lsqrcnt,
|
|
double lambdareg,
|
|
spline1dinterpolant* basis1,
|
|
ae_shared_pool* pool,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state);
|
|
ae_bool _trypexec_spline2d_fastddmfitlayer(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t basecasex,
|
|
ae_int_t tilex0,
|
|
ae_int_t tilex1,
|
|
ae_int_t tilescountx,
|
|
ae_int_t basecasey,
|
|
ae_int_t tiley0,
|
|
ae_int_t tiley1,
|
|
ae_int_t tilescounty,
|
|
ae_int_t maxcoresize,
|
|
ae_int_t interfacesize,
|
|
ae_int_t lsqrcnt,
|
|
double lambdareg,
|
|
spline1dinterpolant* basis1,
|
|
ae_shared_pool* pool,
|
|
spline2dinterpolant* spline, ae_state *_state);
|
|
static void spline2d_blockllsfit(spline2dxdesignmatrix* xdesign,
|
|
ae_int_t lsqrcnt,
|
|
/* Real */ ae_vector* z,
|
|
spline2dfitreport* rep,
|
|
double tss,
|
|
spline2dblockllsbuf* buf,
|
|
ae_state *_state);
|
|
static void spline2d_naivellsfit(sparsematrix* av,
|
|
sparsematrix* ah,
|
|
ae_int_t arows,
|
|
/* Real */ ae_vector* xy,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t lsqrcnt,
|
|
/* Real */ ae_vector* z,
|
|
spline2dfitreport* rep,
|
|
double tss,
|
|
ae_state *_state);
|
|
static ae_int_t spline2d_getcelloffset(ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t blockbandwidth,
|
|
ae_int_t i,
|
|
ae_int_t j,
|
|
ae_state *_state);
|
|
static void spline2d_copycellto(ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t blockbandwidth,
|
|
/* Real */ ae_matrix* blockata,
|
|
ae_int_t i,
|
|
ae_int_t j,
|
|
/* Real */ ae_matrix* dst,
|
|
ae_int_t dst0,
|
|
ae_int_t dst1,
|
|
ae_state *_state);
|
|
static void spline2d_flushtozerocell(ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t blockbandwidth,
|
|
/* Real */ ae_matrix* blockata,
|
|
ae_int_t i,
|
|
ae_int_t j,
|
|
double eps,
|
|
ae_state *_state);
|
|
static void spline2d_blockllsgenerateata(sparsematrix* ah,
|
|
ae_int_t ky0,
|
|
ae_int_t ky1,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Real */ ae_matrix* blockata,
|
|
sreal* mxata,
|
|
ae_state *_state);
|
|
ae_bool _trypexec_spline2d_blockllsgenerateata(sparsematrix* ah,
|
|
ae_int_t ky0,
|
|
ae_int_t ky1,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Real */ ae_matrix* blockata,
|
|
sreal* mxata, ae_state *_state);
|
|
static ae_bool spline2d_blockllscholesky(/* Real */ ae_matrix* blockata,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Real */ ae_matrix* trsmbuf2,
|
|
/* Real */ ae_matrix* cholbuf2,
|
|
/* Real */ ae_vector* cholbuf1,
|
|
ae_state *_state);
|
|
static void spline2d_blockllstrsv(/* Real */ ae_matrix* blockata,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_bool transu,
|
|
/* Real */ ae_vector* b,
|
|
ae_state *_state);
|
|
static void spline2d_computeresidualsfromscratch(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state);
|
|
ae_bool _trypexec_spline2d_computeresidualsfromscratch(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline, ae_state *_state);
|
|
static void spline2d_computeresidualsfromscratchrec(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
ae_int_t chunksize,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline,
|
|
ae_shared_pool* pool,
|
|
ae_state *_state);
|
|
ae_bool _trypexec_spline2d_computeresidualsfromscratchrec(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
ae_int_t chunksize,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline,
|
|
ae_shared_pool* pool, ae_state *_state);
|
|
static void spline2d_reorderdatasetandbuildindex(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Integer */ ae_vector* xyindex,
|
|
/* Integer */ ae_vector* bufi,
|
|
ae_state *_state);
|
|
static void spline2d_rescaledatasetandrefineindex(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Integer */ ae_vector* xyindex,
|
|
/* Integer */ ae_vector* bufi,
|
|
ae_state *_state);
|
|
static void spline2d_expandindexrows(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindexprev,
|
|
ae_int_t row0,
|
|
ae_int_t row1,
|
|
/* Integer */ ae_vector* xyindexnew,
|
|
ae_int_t kxnew,
|
|
ae_int_t kynew,
|
|
ae_bool rootcall,
|
|
ae_state *_state);
|
|
ae_bool _trypexec_spline2d_expandindexrows(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindexprev,
|
|
ae_int_t row0,
|
|
ae_int_t row1,
|
|
/* Integer */ ae_vector* xyindexnew,
|
|
ae_int_t kxnew,
|
|
ae_int_t kynew,
|
|
ae_bool rootcall, ae_state *_state);
|
|
static void spline2d_reorderdatasetandbuildindexrec(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t idx0,
|
|
ae_int_t idx1,
|
|
ae_bool rootcall,
|
|
ae_state *_state);
|
|
ae_bool _trypexec_spline2d_reorderdatasetandbuildindexrec(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t idx0,
|
|
ae_int_t idx1,
|
|
ae_bool rootcall, ae_state *_state);
|
|
static void spline2d_xdesigngenerate(/* Real */ ae_vector* xy,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t kx0,
|
|
ae_int_t kx1,
|
|
ae_int_t kxtotal,
|
|
ae_int_t ky0,
|
|
ae_int_t ky1,
|
|
ae_int_t kytotal,
|
|
ae_int_t d,
|
|
double lambdareg,
|
|
double lambdans,
|
|
spline1dinterpolant* basis1,
|
|
spline2dxdesignmatrix* a,
|
|
ae_state *_state);
|
|
static void spline2d_xdesignmv(spline2dxdesignmatrix* a,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state);
|
|
static void spline2d_xdesignmtv(spline2dxdesignmatrix* a,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state);
|
|
static void spline2d_xdesignblockata(spline2dxdesignmatrix* a,
|
|
/* Real */ ae_matrix* blockata,
|
|
double* mxata,
|
|
ae_state *_state);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RBFV1) || !defined(AE_PARTIAL_BUILD)
|
|
static ae_int_t rbfv1_mxnx = 3;
|
|
static double rbfv1_rbffarradius = 6;
|
|
static double rbfv1_rbfnearradius = 2.1;
|
|
static double rbfv1_rbfmlradius = 3;
|
|
static double rbfv1_minbasecasecost = 100000;
|
|
static ae_bool rbfv1_rbfv1buildlinearmodel(/* 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 rbfv1_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 rbfv1_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);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RBF) || !defined(AE_PARTIAL_BUILD)
|
|
static double rbf_eps = 1.0E-6;
|
|
static double rbf_rbffarradius = 6;
|
|
static ae_int_t rbf_rbffirstversion = 0;
|
|
static ae_int_t rbf_rbfversion2 = 2;
|
|
static void rbf_rbfpreparenonserializablefields(rbfmodel* s,
|
|
ae_state *_state);
|
|
static void rbf_initializev1(ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv1model* s,
|
|
ae_state *_state);
|
|
static void rbf_initializev2(ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv2model* s,
|
|
ae_state *_state);
|
|
static void rbf_clearreportfields(rbfreport* rep, ae_state *_state);
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_INTCOMP) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
#endif
|
|
|
|
#if defined(AE_COMPILE_IDW) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
This function creates buffer structure which can be used to perform
|
|
parallel IDW model evaluations (with one IDW model instance being
|
|
used from multiple threads, as long as different threads use different
|
|
instances of buffer).
|
|
|
|
This buffer object can be used with idwtscalcbuf() function (here "ts"
|
|
stands for "thread-safe", "buf" is a suffix which denotes function which
|
|
reuses previously allocated output space).
|
|
|
|
How to use it:
|
|
* create IDW model structure or load it from file
|
|
* call idwcreatecalcbuffer(), once per thread working with IDW model (you
|
|
should call this function only AFTER model initialization, see below for
|
|
more information)
|
|
* call idwtscalcbuf() from different threads, with each thread working
|
|
with its own copy of buffer object.
|
|
|
|
INPUT PARAMETERS
|
|
S - IDW model
|
|
|
|
OUTPUT PARAMETERS
|
|
Buf - external buffer.
|
|
|
|
|
|
IMPORTANT: buffer object should be used only with IDW model object which
|
|
was used to initialize buffer. Any attempt to use buffer with
|
|
different object is dangerous - you may get memory violation
|
|
error because sizes of internal arrays do not fit to dimensions
|
|
of the IDW structure.
|
|
|
|
IMPORTANT: you should call this function only for model which was built
|
|
with model builder (or unserialized from file). Sizes of some
|
|
internal structures are determined only after model is built,
|
|
so buffer object created before model construction stage will
|
|
be useless (and any attempt to use it will result in exception).
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
void idwcreatecalcbuffer(idwmodel* s,
|
|
idwcalcbuffer* buf,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_idwcalcbuffer_clear(buf);
|
|
|
|
ae_assert(s->nx>=1, "IDWCreateCalcBuffer: integrity check failed", _state);
|
|
ae_assert(s->ny>=1, "IDWCreateCalcBuffer: integrity check failed", _state);
|
|
ae_assert(s->nlayers>=0, "IDWCreateCalcBuffer: integrity check failed", _state);
|
|
ae_assert(s->algotype>=0, "IDWCreateCalcBuffer: integrity check failed", _state);
|
|
if( s->nlayers>=1&&s->algotype!=0 )
|
|
{
|
|
kdtreecreaterequestbuffer(&s->tree, &buf->requestbuffer, _state);
|
|
}
|
|
rvectorsetlengthatleast(&buf->x, s->nx, _state);
|
|
rvectorsetlengthatleast(&buf->y, s->ny, _state);
|
|
rvectorsetlengthatleast(&buf->tsyw, s->ny*ae_maxint(s->nlayers, 1, _state), _state);
|
|
rvectorsetlengthatleast(&buf->tsw, ae_maxint(s->nlayers, 1, _state), _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine creates builder object used to generate IDW model from
|
|
irregularly sampled (scattered) dataset. Multidimensional scalar/vector-
|
|
-valued are supported.
|
|
|
|
Builder object is used to fit model to data as follows:
|
|
* builder object is created with idwbuildercreate() function
|
|
* dataset is added with idwbuildersetpoints() function
|
|
* one of the modern IDW algorithms is chosen with either:
|
|
* idwbuildersetalgomstab() - Multilayer STABilized algorithm (interpolation)
|
|
Alternatively, one of the textbook algorithms can be chosen (not recommended):
|
|
* idwbuildersetalgotextbookshepard() - textbook Shepard algorithm
|
|
* idwbuildersetalgotextbookmodshepard()-textbook modified Shepard algorithm
|
|
* finally, model construction is performed with idwfit() function.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
NX - dimensionality of the argument, NX>=1
|
|
NY - dimensionality of the function being modeled, NY>=1;
|
|
NY=1 corresponds to classic scalar function, NY>=1 corresponds
|
|
to vector-valued function.
|
|
|
|
OUTPUT PARAMETERS:
|
|
State- builder object
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildercreate(ae_int_t nx,
|
|
ae_int_t ny,
|
|
idwbuilder* state,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_idwbuilder_clear(state);
|
|
|
|
ae_assert(nx>=1, "IDWBuilderCreate: NX<=0", _state);
|
|
ae_assert(ny>=1, "IDWBuilderCreate: NY<=0", _state);
|
|
|
|
/*
|
|
* We choose reasonable defaults for the algorithm:
|
|
* * MSTAB algorithm
|
|
* * 12 layers
|
|
* * default radius
|
|
* * default Lambda0
|
|
*/
|
|
state->algotype = 2;
|
|
state->priortermtype = 2;
|
|
rvectorsetlengthatleast(&state->priortermval, ny, _state);
|
|
state->nlayers = idw_defaultnlayers;
|
|
state->r0 = (double)(0);
|
|
state->rdecay = 0.5;
|
|
state->lambda0 = idw_defaultlambda0;
|
|
state->lambdalast = (double)(0);
|
|
state->lambdadecay = 1.0;
|
|
|
|
/*
|
|
* Other parameters, not used but initialized
|
|
*/
|
|
state->shepardp = (double)(0);
|
|
|
|
/*
|
|
* Initial dataset is empty
|
|
*/
|
|
state->npoints = 0;
|
|
state->nx = nx;
|
|
state->ny = ny;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function changes number of layers used by IDW-MSTAB algorithm.
|
|
|
|
The more layers you have, the finer details can be reproduced with IDW
|
|
model. The less layers you have, the less memory and CPU time is consumed
|
|
by the model.
|
|
|
|
Memory consumption grows linearly with layers count, running time grows
|
|
sub-linearly.
|
|
|
|
The default number of layers is 16, which allows you to reproduce details
|
|
at distance down to SRad/65536. You will rarely need to change it.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
NLayers - NLayers>=1, the number of layers used by the model.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetnlayers(idwbuilder* state,
|
|
ae_int_t nlayers,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(nlayers>=1, "IDWBuilderSetNLayers: N<1", _state);
|
|
state->nlayers = nlayers;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function adds dataset to the builder object.
|
|
|
|
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:
|
|
State - builder object
|
|
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
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset, N>=0.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetpoints(idwbuilder* state,
|
|
/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t ew;
|
|
|
|
|
|
ae_assert(n>=0, "IDWBuilderSetPoints: N<0", _state);
|
|
ae_assert(xy->rows>=n, "IDWBuilderSetPoints: Rows(XY)<N", _state);
|
|
ae_assert(n==0||xy->cols>=state->nx+state->ny, "IDWBuilderSetPoints: Cols(XY)<NX+NY", _state);
|
|
ae_assert(apservisfinitematrix(xy, n, state->nx+state->ny, _state), "IDWBuilderSetPoints: XY contains infinite or NaN values!", _state);
|
|
state->npoints = n;
|
|
ew = state->nx+state->ny;
|
|
rvectorsetlengthatleast(&state->xy, n*ew, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ew-1; j++)
|
|
{
|
|
state->xy.ptr.p_double[i*ew+j] = xy->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets IDW model construction algorithm to the Multilayer
|
|
Stabilized IDW method (IDW-MSTAB), a latest incarnation of the inverse
|
|
distance weighting interpolation which fixes shortcomings of the original
|
|
and modified Shepard's variants.
|
|
|
|
The distinctive features of IDW-MSTAB are:
|
|
1) exact interpolation is pursued (as opposed to fitting and noise
|
|
suppression)
|
|
2) improved robustness when compared with that of other algorithms:
|
|
* MSTAB shows almost no strange fitting artifacts like ripples and
|
|
sharp spikes (unlike N-dimensional splines and HRBFs)
|
|
* MSTAB does not return function values far from the interval spanned
|
|
by the dataset; say, if all your points have |f|<=1, you can be sure
|
|
that model value won't deviate too much from [-1,+1]
|
|
3) good model construction time competing with that of HRBFs and bicubic
|
|
splines
|
|
4) ability to work with any number of dimensions, starting from NX=1
|
|
|
|
The drawbacks of IDW-MSTAB (and all IDW algorithms in general) are:
|
|
1) dependence of the model evaluation time on the search radius
|
|
2) bad extrapolation properties, models built by this method are usually
|
|
conservative in their predictions
|
|
|
|
Thus, IDW-MSTAB is a good "default" option if you want to perform
|
|
scattered multidimensional interpolation. Although it has its drawbacks,
|
|
it is easy to use and robust, which makes it a good first step.
|
|
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
SRad - initial search radius, SRad>0 is required. A model value
|
|
is obtained by "smart" averaging of the dataset points
|
|
within search radius.
|
|
|
|
NOTE 1: IDW interpolation can correctly handle ANY dataset, including
|
|
datasets with non-distinct points. In case non-distinct points are
|
|
found, an average value for this point will be calculated.
|
|
|
|
NOTE 2: the memory requirements for model storage are O(NPoints*NLayers).
|
|
The model construction needs twice as much memory as model storage.
|
|
|
|
NOTE 3: by default 16 IDW layers are built which is enough for most cases.
|
|
You can change this parameter with idwbuildersetnlayers() method.
|
|
Larger values may be necessary if you need to reproduce extrafine
|
|
details at distances smaller than SRad/65536. Smaller value may
|
|
be necessary if you have to save memory and computing time, and
|
|
ready to sacrifice some model quality.
|
|
|
|
|
|
ALGORITHM DESCRIPTION
|
|
|
|
ALGLIB implementation of IDW is somewhat similar to the modified Shepard's
|
|
method (one with search radius R) but overcomes several of its drawbacks,
|
|
namely:
|
|
1) a tendency to show stepwise behavior for uniform datasets
|
|
2) a tendency to show terrible interpolation properties for highly
|
|
nonuniform datasets which often arise in geospatial tasks
|
|
(function values are densely sampled across multiple separated
|
|
"tracks")
|
|
|
|
IDW-MSTAB method performs several passes over dataset and builds a sequence
|
|
of progressively refined IDW models (layers), which starts from one with
|
|
largest search radius SRad and continues to smaller search radii until
|
|
required number of layers is built. Highest layers reproduce global
|
|
behavior of the target function at larger distances whilst lower layers
|
|
reproduce fine details at smaller distances.
|
|
|
|
Each layer is an IDW model built with following modifications:
|
|
* weights go to zero when distance approach to the current search radius
|
|
* an additional regularizing term is added to the distance: w=1/(d^2+lambda)
|
|
* an additional fictional term with unit weight and zero function value is
|
|
added in order to promote continuity properties at the isolated and
|
|
boundary points
|
|
|
|
By default, 16 layers is built, which is enough for most cases. You can
|
|
change this parameter with idwbuildersetnlayers() method.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetalgomstab(idwbuilder* state,
|
|
double srad,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(srad, _state), "IDWBuilderSetAlgoMSTAB: SRad is not finite", _state);
|
|
ae_assert(ae_fp_greater(srad,(double)(0)), "IDWBuilderSetAlgoMSTAB: SRad<=0", _state);
|
|
|
|
/*
|
|
* Set algorithm
|
|
*/
|
|
state->algotype = 2;
|
|
|
|
/*
|
|
* Set options
|
|
*/
|
|
state->r0 = srad;
|
|
state->rdecay = 0.5;
|
|
state->lambda0 = idw_defaultlambda0;
|
|
state->lambdalast = (double)(0);
|
|
state->lambdadecay = 1.0;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets IDW model construction algorithm to the textbook
|
|
Shepard's algorithm with custom (user-specified) power parameter.
|
|
|
|
IMPORTANT: we do NOT recommend using textbook IDW algorithms because they
|
|
have terrible interpolation properties. Use MSTAB in all cases.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
P - power parameter, P>0; good value to start with is 2.0
|
|
|
|
NOTE 1: IDW interpolation can correctly handle ANY dataset, including
|
|
datasets with non-distinct points. In case non-distinct points are
|
|
found, an average value for this point will be calculated.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetalgotextbookshepard(idwbuilder* state,
|
|
double p,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(p, _state), "IDWBuilderSetAlgoShepard: P is not finite", _state);
|
|
ae_assert(ae_fp_greater(p,(double)(0)), "IDWBuilderSetAlgoShepard: P<=0", _state);
|
|
|
|
/*
|
|
* Set algorithm and options
|
|
*/
|
|
state->algotype = 0;
|
|
state->shepardp = p;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets IDW model construction algorithm to the 'textbook'
|
|
modified Shepard's algorithm with user-specified search radius.
|
|
|
|
IMPORTANT: we do NOT recommend using textbook IDW algorithms because they
|
|
have terrible interpolation properties. Use MSTAB in all cases.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
R - search radius
|
|
|
|
NOTE 1: IDW interpolation can correctly handle ANY dataset, including
|
|
datasets with non-distinct points. In case non-distinct points are
|
|
found, an average value for this point will be calculated.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetalgotextbookmodshepard(idwbuilder* state,
|
|
double r,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(r, _state), "IDWBuilderSetAlgoModShepard: R is not finite", _state);
|
|
ae_assert(ae_fp_greater(r,(double)(0)), "IDWBuilderSetAlgoModShepard: R<=0", _state);
|
|
|
|
/*
|
|
* Set algorithm and options
|
|
*/
|
|
state->algotype = 1;
|
|
state->r0 = r;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets prior term (model value at infinity) as user-specified
|
|
value.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
V - value for user-defined prior
|
|
|
|
NOTE: for vector-valued models all components of the prior are set to same
|
|
user-specified value
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetuserterm(idwbuilder* state, double v, ae_state *_state)
|
|
{
|
|
ae_int_t j;
|
|
|
|
|
|
ae_assert(ae_isfinite(v, _state), "IDWBuilderSetUserTerm: infinite/NAN value passed", _state);
|
|
state->priortermtype = 0;
|
|
for(j=0; j<=state->ny-1; j++)
|
|
{
|
|
state->priortermval.ptr.p_double[j] = v;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets constant prior term (model value at infinity).
|
|
|
|
Constant prior term is determined as mean value over dataset.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetconstterm(idwbuilder* state, ae_state *_state)
|
|
{
|
|
|
|
|
|
state->priortermtype = 2;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets zero prior term (model value at infinity).
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwbuildersetzeroterm(idwbuilder* state, ae_state *_state)
|
|
{
|
|
|
|
|
|
state->priortermtype = 3;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW interpolation: scalar target, 1-dimensional argument
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW interpolant built with IDW builder
|
|
X0 - argument value
|
|
|
|
Result:
|
|
IDW interpolant S(X0)
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc1(idwmodel* s, double x0, ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
ae_assert(s->nx==1, "IDWCalc1: S.NX<>1", _state);
|
|
ae_assert(s->ny==1, "IDWCalc1: S.NY<>1", _state);
|
|
ae_assert(ae_isfinite(x0, _state), "IDWCalc1: X0 is INF or NAN", _state);
|
|
s->buffer.x.ptr.p_double[0] = x0;
|
|
idwtscalcbuf(s, &s->buffer, &s->buffer.x, &s->buffer.y, _state);
|
|
result = s->buffer.y.ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW interpolation: scalar target, 2-dimensional argument
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW interpolant built with IDW builder
|
|
X0, X1 - argument value
|
|
|
|
Result:
|
|
IDW interpolant S(X0,X1)
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc2(idwmodel* s, double x0, double x1, ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
ae_assert(s->nx==2, "IDWCalc2: S.NX<>2", _state);
|
|
ae_assert(s->ny==1, "IDWCalc2: S.NY<>1", _state);
|
|
ae_assert(ae_isfinite(x0, _state), "IDWCalc2: X0 is INF or NAN", _state);
|
|
ae_assert(ae_isfinite(x1, _state), "IDWCalc2: X1 is INF or NAN", _state);
|
|
s->buffer.x.ptr.p_double[0] = x0;
|
|
s->buffer.x.ptr.p_double[1] = x1;
|
|
idwtscalcbuf(s, &s->buffer, &s->buffer.x, &s->buffer.y, _state);
|
|
result = s->buffer.y.ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
IDW interpolation: scalar target, 3-dimensional argument
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW interpolant built with IDW builder
|
|
X0,X1,X2- argument value
|
|
|
|
Result:
|
|
IDW interpolant S(X0,X1,X2)
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double idwcalc3(idwmodel* s,
|
|
double x0,
|
|
double x1,
|
|
double x2,
|
|
ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
ae_assert(s->nx==3, "IDWCalc3: S.NX<>3", _state);
|
|
ae_assert(s->ny==1, "IDWCalc3: S.NY<>1", _state);
|
|
ae_assert(ae_isfinite(x0, _state), "IDWCalc3: X0 is INF or NAN", _state);
|
|
ae_assert(ae_isfinite(x1, _state), "IDWCalc3: X1 is INF or NAN", _state);
|
|
ae_assert(ae_isfinite(x2, _state), "IDWCalc3: X2 is INF or NAN", _state);
|
|
s->buffer.x.ptr.p_double[0] = x0;
|
|
s->buffer.x.ptr.p_double[1] = x1;
|
|
s->buffer.x.ptr.p_double[2] = x2;
|
|
idwtscalcbuf(s, &s->buffer, &s->buffer.x, &s->buffer.y, _state);
|
|
result = s->buffer.y.ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the IDW 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 idwcalc1(),
|
|
idwcalc2() or idwcalc3().
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW 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 will be
|
|
reallocated after call to this function. In case you want
|
|
to reuse previously allocated Y, you may use idwcalcbuf(),
|
|
which reallocates Y only when it is too small.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwcalc(idwmodel* s,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
|
|
ae_vector_clear(y);
|
|
|
|
idwtscalcbuf(s, &s->buffer, x, y, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the IDW model at the given point.
|
|
|
|
Same as idwcalc(), but does not reallocate Y when in is large enough to
|
|
store function values.
|
|
|
|
NOTE: this function modifies internal temporaries of the IDW model, thus
|
|
IT IS NOT THREAD-SAFE! If you want to perform parallel model
|
|
evaluation from the multiple threads, use idwtscalcbuf() with per-
|
|
thread buffer object.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW 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 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwcalcbuf(idwmodel* s,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
idwtscalcbuf(s, &s->buffer, x, y, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the IDW model at the given point, using
|
|
external buffer object (internal temporaries of IDW model are not
|
|
modified).
|
|
|
|
This function allows to use same IDW model object in different threads,
|
|
assuming that different threads use different instances of the buffer
|
|
structure.
|
|
|
|
INPUT PARAMETERS:
|
|
S - IDW model, may be shared between different threads
|
|
Buf - buffer object created for this particular instance of IDW
|
|
model with idwcreatecalcbuffer().
|
|
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 idwtscalcbuf(idwmodel* s,
|
|
idwcalcbuffer* buf,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t ew;
|
|
ae_int_t k;
|
|
ae_int_t layeridx;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
ae_int_t npoints;
|
|
double v;
|
|
double vv;
|
|
double f;
|
|
double p;
|
|
double r;
|
|
double eps;
|
|
double lambdacur;
|
|
double lambdadecay;
|
|
double invrdecay;
|
|
double invr;
|
|
ae_bool fastcalcpossible;
|
|
double wf0;
|
|
double ws0;
|
|
double wf1;
|
|
double ws1;
|
|
|
|
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
ae_assert(x->cnt>=nx, "IDWTsCalcBuf: Length(X)<NX", _state);
|
|
ae_assert(isfinitevector(x, nx, _state), "IDWTsCalcBuf: X contains infinite or NaN values", _state);
|
|
|
|
/*
|
|
* Avoid spurious compiler warnings
|
|
*/
|
|
wf0 = (double)(0);
|
|
ws0 = (double)(0);
|
|
wf1 = (double)(0);
|
|
ws1 = (double)(0);
|
|
|
|
/*
|
|
* Allocate output
|
|
*/
|
|
if( y->cnt<ny )
|
|
{
|
|
ae_vector_set_length(y, ny, _state);
|
|
}
|
|
|
|
/*
|
|
* Quick exit for NLayers=0 (no dataset)
|
|
*/
|
|
if( s->nlayers==0 )
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = s->globalprior.ptr.p_double[j];
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Textbook Shepard's method
|
|
*/
|
|
if( s->algotype==0 )
|
|
{
|
|
npoints = s->npoints;
|
|
ae_assert(npoints>0, "IDWTsCalcBuf: integrity check failed", _state);
|
|
eps = 1.0E-50;
|
|
ew = nx+ny;
|
|
p = s->shepardp;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = (double)(0);
|
|
buf->tsyw.ptr.p_double[j] = eps;
|
|
}
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Compute squared distance
|
|
*/
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
vv = s->shepardxy.ptr.p_double[i*ew+j]-x->ptr.p_double[j];
|
|
v = v+vv*vv;
|
|
}
|
|
|
|
/*
|
|
* Compute weight (with small regularizing addition)
|
|
*/
|
|
v = ae_pow(v, p*0.5, _state);
|
|
v = 1/(eps+v);
|
|
|
|
/*
|
|
* Accumulate
|
|
*/
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = y->ptr.p_double[j]+v*s->shepardxy.ptr.p_double[i*ew+nx+j];
|
|
buf->tsyw.ptr.p_double[j] = buf->tsyw.ptr.p_double[j]+v;
|
|
}
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = y->ptr.p_double[j]/buf->tsyw.ptr.p_double[j]+s->globalprior.ptr.p_double[j];
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Textbook modified Shepard's method
|
|
*/
|
|
if( s->algotype==1 )
|
|
{
|
|
eps = 1.0E-50;
|
|
r = s->r0;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = (double)(0);
|
|
buf->tsyw.ptr.p_double[j] = eps;
|
|
}
|
|
k = kdtreetsqueryrnn(&s->tree, &buf->requestbuffer, x, r, ae_true, _state);
|
|
kdtreetsqueryresultsxy(&s->tree, &buf->requestbuffer, &buf->tsxy, _state);
|
|
kdtreetsqueryresultsdistances(&s->tree, &buf->requestbuffer, &buf->tsdist, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
v = buf->tsdist.ptr.p_double[i];
|
|
v = (r-v)/(r*v+eps);
|
|
v = v*v;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = y->ptr.p_double[j]+v*buf->tsxy.ptr.pp_double[i][nx+j];
|
|
buf->tsyw.ptr.p_double[j] = buf->tsyw.ptr.p_double[j]+v;
|
|
}
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = y->ptr.p_double[j]/buf->tsyw.ptr.p_double[j]+s->globalprior.ptr.p_double[j];
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* MSTAB
|
|
*/
|
|
if( s->algotype==2 )
|
|
{
|
|
ae_assert(ae_fp_eq(idw_w0,(double)(1)), "IDWTsCalcBuf: unexpected W0, integrity check failed", _state);
|
|
invrdecay = 1/s->rdecay;
|
|
invr = 1/s->r0;
|
|
lambdadecay = s->lambdadecay;
|
|
fastcalcpossible = (ny==1&&s->nlayers>=3)&&ae_fp_eq(lambdadecay,(double)(1));
|
|
if( fastcalcpossible )
|
|
{
|
|
|
|
/*
|
|
* Important special case, NY=1, no lambda-decay,
|
|
* we can perform optimized fast evaluation
|
|
*/
|
|
wf0 = (double)(0);
|
|
ws0 = idw_w0;
|
|
wf1 = (double)(0);
|
|
ws1 = idw_w0;
|
|
for(j=0; j<=s->nlayers-1; j++)
|
|
{
|
|
buf->tsyw.ptr.p_double[j] = (double)(0);
|
|
buf->tsw.ptr.p_double[j] = idw_w0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Setup variables for generic evaluation path
|
|
*/
|
|
for(j=0; j<=ny*s->nlayers-1; j++)
|
|
{
|
|
buf->tsyw.ptr.p_double[j] = (double)(0);
|
|
}
|
|
for(j=0; j<=s->nlayers-1; j++)
|
|
{
|
|
buf->tsw.ptr.p_double[j] = idw_w0;
|
|
}
|
|
}
|
|
k = kdtreetsqueryrnnu(&s->tree, &buf->requestbuffer, x, s->r0, ae_true, _state);
|
|
kdtreetsqueryresultsxy(&s->tree, &buf->requestbuffer, &buf->tsxy, _state);
|
|
kdtreetsqueryresultsdistances(&s->tree, &buf->requestbuffer, &buf->tsdist, _state);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
lambdacur = s->lambda0;
|
|
vv = buf->tsdist.ptr.p_double[i]*invr;
|
|
if( fastcalcpossible )
|
|
{
|
|
|
|
/*
|
|
* Important special case, fast evaluation possible
|
|
*/
|
|
v = vv*vv;
|
|
v = (1-v)*(1-v)/(v+lambdacur);
|
|
f = buf->tsxy.ptr.pp_double[i][nx+0];
|
|
wf0 = wf0+v*f;
|
|
ws0 = ws0+v;
|
|
vv = vv*invrdecay;
|
|
if( vv>=1.0 )
|
|
{
|
|
continue;
|
|
}
|
|
v = vv*vv;
|
|
v = (1-v)*(1-v)/(v+lambdacur);
|
|
f = buf->tsxy.ptr.pp_double[i][nx+1];
|
|
wf1 = wf1+v*f;
|
|
ws1 = ws1+v;
|
|
vv = vv*invrdecay;
|
|
if( vv>=1.0 )
|
|
{
|
|
continue;
|
|
}
|
|
for(layeridx=2; layeridx<=s->nlayers-1; layeridx++)
|
|
{
|
|
if( layeridx==s->nlayers-1 )
|
|
{
|
|
lambdacur = s->lambdalast;
|
|
}
|
|
v = vv*vv;
|
|
v = (1-v)*(1-v)/(v+lambdacur);
|
|
f = buf->tsxy.ptr.pp_double[i][nx+layeridx];
|
|
buf->tsyw.ptr.p_double[layeridx] = buf->tsyw.ptr.p_double[layeridx]+v*f;
|
|
buf->tsw.ptr.p_double[layeridx] = buf->tsw.ptr.p_double[layeridx]+v;
|
|
vv = vv*invrdecay;
|
|
if( vv>=1.0 )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* General case
|
|
*/
|
|
for(layeridx=0; layeridx<=s->nlayers-1; layeridx++)
|
|
{
|
|
if( layeridx==s->nlayers-1 )
|
|
{
|
|
lambdacur = s->lambdalast;
|
|
}
|
|
if( vv>=1.0 )
|
|
{
|
|
break;
|
|
}
|
|
v = vv*vv;
|
|
v = (1-v)*(1-v)/(v+lambdacur);
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
f = buf->tsxy.ptr.pp_double[i][nx+layeridx*ny+j];
|
|
buf->tsyw.ptr.p_double[layeridx*ny+j] = buf->tsyw.ptr.p_double[layeridx*ny+j]+v*f;
|
|
}
|
|
buf->tsw.ptr.p_double[layeridx] = buf->tsw.ptr.p_double[layeridx]+v;
|
|
lambdacur = lambdacur*lambdadecay;
|
|
vv = vv*invrdecay;
|
|
}
|
|
}
|
|
}
|
|
if( fastcalcpossible )
|
|
{
|
|
|
|
/*
|
|
* Important special case, finalize evaluations
|
|
*/
|
|
buf->tsyw.ptr.p_double[0] = wf0;
|
|
buf->tsw.ptr.p_double[0] = ws0;
|
|
buf->tsyw.ptr.p_double[1] = wf1;
|
|
buf->tsw.ptr.p_double[1] = ws1;
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = s->globalprior.ptr.p_double[j];
|
|
}
|
|
for(layeridx=0; layeridx<=s->nlayers-1; layeridx++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = y->ptr.p_double[j]+buf->tsyw.ptr.p_double[layeridx*ny+j]/buf->tsw.ptr.p_double[layeridx];
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*/
|
|
ae_assert(ae_false, "IDWTsCalcBuf: unexpected AlgoType", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function fits IDW model to the dataset using current IDW construction
|
|
algorithm. A model being built and fitting report are returned.
|
|
|
|
INPUT PARAMETERS:
|
|
State - builder object
|
|
|
|
OUTPUT PARAMETERS:
|
|
Model - an IDW model built with current algorithm
|
|
Rep - model fitting report, fields of this structure contain
|
|
information about average fitting errors.
|
|
|
|
NOTE: although IDW-MSTAB algorithm is an interpolation method, i.e. it
|
|
tries to fit the model exactly, it can handle datasets with non-
|
|
distinct points which can not be fit exactly; in such cases least-
|
|
squares fitting is performed.
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwfit(idwbuilder* state,
|
|
idwmodel* model,
|
|
idwreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t i0;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t layeridx;
|
|
ae_int_t srcidx;
|
|
double v;
|
|
double vv;
|
|
ae_int_t npoints;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
double rcur;
|
|
double lambdacur;
|
|
double rss;
|
|
double tss;
|
|
|
|
_idwmodel_clear(model);
|
|
_idwreport_clear(rep);
|
|
|
|
nx = state->nx;
|
|
ny = state->ny;
|
|
npoints = state->npoints;
|
|
|
|
/*
|
|
* Clear report fields
|
|
*/
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rep->r2 = 1.0;
|
|
|
|
/*
|
|
* Quick exit for empty dataset
|
|
*/
|
|
if( state->npoints==0 )
|
|
{
|
|
model->nx = nx;
|
|
model->ny = ny;
|
|
ae_vector_set_length(&model->globalprior, ny, _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
model->globalprior.ptr.p_double[i] = (double)(0);
|
|
}
|
|
model->algotype = 0;
|
|
model->nlayers = 0;
|
|
model->r0 = (double)(1);
|
|
model->rdecay = 0.5;
|
|
model->lambda0 = (double)(0);
|
|
model->lambdalast = (double)(0);
|
|
model->lambdadecay = (double)(1);
|
|
model->shepardp = (double)(2);
|
|
model->npoints = 0;
|
|
idwcreatecalcbuffer(model, &model->buffer, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Compute temporaries which will be required later:
|
|
* * global mean
|
|
*/
|
|
ae_assert(state->npoints>0, "IDWFit: integrity check failed", _state);
|
|
rvectorsetlengthatleast(&state->tmpmean, ny, _state);
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
state->tmpmean.ptr.p_double[j] = (double)(0);
|
|
}
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
state->tmpmean.ptr.p_double[j] = state->tmpmean.ptr.p_double[j]+state->xy.ptr.p_double[i*(nx+ny)+nx+j];
|
|
}
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
state->tmpmean.ptr.p_double[j] = state->tmpmean.ptr.p_double[j]/npoints;
|
|
}
|
|
|
|
/*
|
|
* Compute global prior
|
|
*
|
|
* NOTE: for original Shepard's method it is always mean value
|
|
*/
|
|
rvectorsetlengthatleast(&model->globalprior, ny, _state);
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
model->globalprior.ptr.p_double[j] = state->tmpmean.ptr.p_double[j];
|
|
}
|
|
if( state->algotype!=0 )
|
|
{
|
|
|
|
/*
|
|
* Algorithm is set to one of the "advanced" versions with search
|
|
* radius which can handle non-mean prior term
|
|
*/
|
|
if( state->priortermtype==0 )
|
|
{
|
|
|
|
/*
|
|
* User-specified prior
|
|
*/
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
model->globalprior.ptr.p_double[j] = state->priortermval.ptr.p_double[j];
|
|
}
|
|
}
|
|
if( state->priortermtype==3 )
|
|
{
|
|
|
|
/*
|
|
* Zero prior
|
|
*/
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
model->globalprior.ptr.p_double[j] = (double)(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Textbook Shepard
|
|
*/
|
|
if( state->algotype==0 )
|
|
{
|
|
|
|
/*
|
|
* Initialize model
|
|
*/
|
|
model->algotype = 0;
|
|
model->nx = nx;
|
|
model->ny = ny;
|
|
model->nlayers = 1;
|
|
model->r0 = (double)(1);
|
|
model->rdecay = 0.5;
|
|
model->lambda0 = (double)(0);
|
|
model->lambdalast = (double)(0);
|
|
model->lambdadecay = (double)(1);
|
|
model->shepardp = state->shepardp;
|
|
|
|
/*
|
|
* Copy dataset
|
|
*/
|
|
rvectorsetlengthatleast(&model->shepardxy, npoints*(nx+ny), _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
model->shepardxy.ptr.p_double[i*(nx+ny)+j] = state->xy.ptr.p_double[i*(nx+ny)+j];
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
model->shepardxy.ptr.p_double[i*(nx+ny)+nx+j] = state->xy.ptr.p_double[i*(nx+ny)+nx+j]-model->globalprior.ptr.p_double[j];
|
|
}
|
|
}
|
|
model->npoints = npoints;
|
|
|
|
/*
|
|
* Prepare internal buffer
|
|
* Evaluate report fields
|
|
*/
|
|
idwcreatecalcbuffer(model, &model->buffer, _state);
|
|
idw_errormetricsviacalc(state, model, rep, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Textbook modified Shepard's method
|
|
*/
|
|
if( state->algotype==1 )
|
|
{
|
|
|
|
/*
|
|
* Initialize model
|
|
*/
|
|
model->algotype = 1;
|
|
model->nx = nx;
|
|
model->ny = ny;
|
|
model->nlayers = 1;
|
|
model->r0 = state->r0;
|
|
model->rdecay = (double)(1);
|
|
model->lambda0 = (double)(0);
|
|
model->lambdalast = (double)(0);
|
|
model->lambdadecay = (double)(1);
|
|
model->shepardp = (double)(0);
|
|
|
|
/*
|
|
* Build kd-tree search structure
|
|
*/
|
|
rmatrixsetlengthatleast(&state->tmpxy, npoints, nx+ny, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
state->tmpxy.ptr.pp_double[i][j] = state->xy.ptr.p_double[i*(nx+ny)+j];
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
state->tmpxy.ptr.pp_double[i][nx+j] = state->xy.ptr.p_double[i*(nx+ny)+nx+j]-model->globalprior.ptr.p_double[j];
|
|
}
|
|
}
|
|
kdtreebuild(&state->tmpxy, npoints, nx, ny, 2, &model->tree, _state);
|
|
|
|
/*
|
|
* Prepare internal buffer
|
|
* Evaluate report fields
|
|
*/
|
|
idwcreatecalcbuffer(model, &model->buffer, _state);
|
|
idw_errormetricsviacalc(state, model, rep, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* MSTAB algorithm
|
|
*/
|
|
if( state->algotype==2 )
|
|
{
|
|
ae_assert(state->nlayers>=1, "IDWFit: integrity check failed", _state);
|
|
|
|
/*
|
|
* Initialize model
|
|
*/
|
|
model->algotype = 2;
|
|
model->nx = nx;
|
|
model->ny = ny;
|
|
model->nlayers = state->nlayers;
|
|
model->r0 = state->r0;
|
|
model->rdecay = 0.5;
|
|
model->lambda0 = state->lambda0;
|
|
model->lambdadecay = 1.0;
|
|
model->lambdalast = idw_meps;
|
|
model->shepardp = (double)(0);
|
|
|
|
/*
|
|
* Build kd-tree search structure,
|
|
* prepare input residuals for the first layer of the model
|
|
*/
|
|
rmatrixsetlengthatleast(&state->tmpxy, npoints, nx, _state);
|
|
rmatrixsetlengthatleast(&state->tmplayers, npoints, nx+ny*(state->nlayers+1), _state);
|
|
ivectorsetlengthatleast(&state->tmptags, npoints, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = state->xy.ptr.p_double[i*(nx+ny)+j];
|
|
state->tmpxy.ptr.pp_double[i][j] = v;
|
|
state->tmplayers.ptr.pp_double[i][j] = v;
|
|
}
|
|
state->tmptags.ptr.p_int[i] = i;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
state->tmplayers.ptr.pp_double[i][nx+j] = state->xy.ptr.p_double[i*(nx+ny)+nx+j]-model->globalprior.ptr.p_double[j];
|
|
}
|
|
}
|
|
kdtreebuildtagged(&state->tmpxy, &state->tmptags, npoints, nx, 0, 2, &state->tmptree, _state);
|
|
|
|
/*
|
|
* Iteratively build layer by layer
|
|
*/
|
|
rvectorsetlengthatleast(&state->tmpx, nx, _state);
|
|
rvectorsetlengthatleast(&state->tmpwy, ny, _state);
|
|
rvectorsetlengthatleast(&state->tmpw, ny, _state);
|
|
for(layeridx=0; layeridx<=state->nlayers-1; layeridx++)
|
|
{
|
|
|
|
/*
|
|
* Determine layer metrics
|
|
*/
|
|
rcur = model->r0*ae_pow(model->rdecay, (double)(layeridx), _state);
|
|
lambdacur = model->lambda0*ae_pow(model->lambdadecay, (double)(layeridx), _state);
|
|
if( layeridx==state->nlayers-1 )
|
|
{
|
|
lambdacur = model->lambdalast;
|
|
}
|
|
|
|
/*
|
|
* For each point compute residual from fitting with current layer
|
|
*/
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
state->tmpx.ptr.p_double[j] = state->tmplayers.ptr.pp_double[i][j];
|
|
}
|
|
k = kdtreequeryrnn(&state->tmptree, &state->tmpx, rcur, ae_true, _state);
|
|
kdtreequeryresultstags(&state->tmptree, &state->tmptags, _state);
|
|
kdtreequeryresultsdistances(&state->tmptree, &state->tmpdist, _state);
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
state->tmpwy.ptr.p_double[j] = (double)(0);
|
|
state->tmpw.ptr.p_double[j] = idw_w0;
|
|
}
|
|
for(i0=0; i0<=k-1; i0++)
|
|
{
|
|
vv = state->tmpdist.ptr.p_double[i0]/rcur;
|
|
vv = vv*vv;
|
|
v = (1-vv)*(1-vv)/(vv+lambdacur);
|
|
srcidx = state->tmptags.ptr.p_int[i0];
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
state->tmpwy.ptr.p_double[j] = state->tmpwy.ptr.p_double[j]+v*state->tmplayers.ptr.pp_double[srcidx][nx+layeridx*ny+j];
|
|
state->tmpw.ptr.p_double[j] = state->tmpw.ptr.p_double[j]+v;
|
|
}
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v = state->tmplayers.ptr.pp_double[i][nx+layeridx*ny+j];
|
|
state->tmplayers.ptr.pp_double[i][nx+(layeridx+1)*ny+j] = v-state->tmpwy.ptr.p_double[j]/state->tmpw.ptr.p_double[j];
|
|
}
|
|
}
|
|
}
|
|
kdtreebuild(&state->tmplayers, npoints, nx, ny*state->nlayers, 2, &model->tree, _state);
|
|
|
|
/*
|
|
* Evaluate report fields
|
|
*/
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rss = (double)(0);
|
|
tss = (double)(0);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v = ae_fabs(state->tmplayers.ptr.pp_double[i][nx+state->nlayers*ny+j], _state);
|
|
rep->rmserror = rep->rmserror+v*v;
|
|
rep->avgerror = rep->avgerror+v;
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v, _state), _state);
|
|
rss = rss+v*v;
|
|
tss = tss+ae_sqr(state->xy.ptr.p_double[i*(nx+ny)+nx+j]-state->tmpmean.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/(npoints*ny), _state);
|
|
rep->avgerror = rep->avgerror/(npoints*ny);
|
|
rep->r2 = 1.0-rss/coalesce(tss, 1.0, _state);
|
|
|
|
/*
|
|
* Prepare internal buffer
|
|
*/
|
|
idwcreatecalcbuffer(model, &model->buffer, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Unknown algorithm
|
|
*/
|
|
ae_assert(ae_false, "IDWFit: integrity check failed, unexpected algorithm", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: allocation
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwalloc(ae_serializer* s, idwmodel* model, ae_state *_state)
|
|
{
|
|
ae_bool processed;
|
|
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_alloc_entry(s);
|
|
|
|
/*
|
|
* Algorithm type and fields which are set for all algorithms
|
|
*/
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
allocrealarray(s, &model->globalprior, -1, _state);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
|
|
/*
|
|
* Algorithm-specific fields
|
|
*/
|
|
processed = ae_false;
|
|
if( model->algotype==0 )
|
|
{
|
|
ae_serializer_alloc_entry(s);
|
|
allocrealarray(s, &model->shepardxy, -1, _state);
|
|
processed = ae_true;
|
|
}
|
|
if( model->algotype>0 )
|
|
{
|
|
kdtreealloc(s, &model->tree, _state);
|
|
processed = ae_true;
|
|
}
|
|
ae_assert(processed, "IDW: integrity check failed during serialization", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: serialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwserialize(ae_serializer* s, idwmodel* model, ae_state *_state)
|
|
{
|
|
ae_bool processed;
|
|
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_serialize_int(s, getidwserializationcode(_state), _state);
|
|
|
|
/*
|
|
* Algorithm type and fields which are set for all algorithms
|
|
*/
|
|
ae_serializer_serialize_int(s, model->algotype, _state);
|
|
ae_serializer_serialize_int(s, model->nx, _state);
|
|
ae_serializer_serialize_int(s, model->ny, _state);
|
|
serializerealarray(s, &model->globalprior, -1, _state);
|
|
ae_serializer_serialize_int(s, model->nlayers, _state);
|
|
ae_serializer_serialize_double(s, model->r0, _state);
|
|
ae_serializer_serialize_double(s, model->rdecay, _state);
|
|
ae_serializer_serialize_double(s, model->lambda0, _state);
|
|
ae_serializer_serialize_double(s, model->lambdalast, _state);
|
|
ae_serializer_serialize_double(s, model->lambdadecay, _state);
|
|
ae_serializer_serialize_double(s, model->shepardp, _state);
|
|
|
|
/*
|
|
* Algorithm-specific fields
|
|
*/
|
|
processed = ae_false;
|
|
if( model->algotype==0 )
|
|
{
|
|
ae_serializer_serialize_int(s, model->npoints, _state);
|
|
serializerealarray(s, &model->shepardxy, -1, _state);
|
|
processed = ae_true;
|
|
}
|
|
if( model->algotype>0 )
|
|
{
|
|
kdtreeserialize(s, &model->tree, _state);
|
|
processed = ae_true;
|
|
}
|
|
ae_assert(processed, "IDW: integrity check failed during serialization", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: unserialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void idwunserialize(ae_serializer* s, idwmodel* model, ae_state *_state)
|
|
{
|
|
ae_bool processed;
|
|
ae_int_t scode;
|
|
|
|
_idwmodel_clear(model);
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_unserialize_int(s, &scode, _state);
|
|
ae_assert(scode==getidwserializationcode(_state), "IDWUnserialize: stream header corrupted", _state);
|
|
|
|
/*
|
|
* Algorithm type and fields which are set for all algorithms
|
|
*/
|
|
ae_serializer_unserialize_int(s, &model->algotype, _state);
|
|
ae_serializer_unserialize_int(s, &model->nx, _state);
|
|
ae_serializer_unserialize_int(s, &model->ny, _state);
|
|
unserializerealarray(s, &model->globalprior, _state);
|
|
ae_serializer_unserialize_int(s, &model->nlayers, _state);
|
|
ae_serializer_unserialize_double(s, &model->r0, _state);
|
|
ae_serializer_unserialize_double(s, &model->rdecay, _state);
|
|
ae_serializer_unserialize_double(s, &model->lambda0, _state);
|
|
ae_serializer_unserialize_double(s, &model->lambdalast, _state);
|
|
ae_serializer_unserialize_double(s, &model->lambdadecay, _state);
|
|
ae_serializer_unserialize_double(s, &model->shepardp, _state);
|
|
|
|
/*
|
|
* Algorithm-specific fields
|
|
*/
|
|
processed = ae_false;
|
|
if( model->algotype==0 )
|
|
{
|
|
ae_serializer_unserialize_int(s, &model->npoints, _state);
|
|
unserializerealarray(s, &model->shepardxy, _state);
|
|
processed = ae_true;
|
|
}
|
|
if( model->algotype>0 )
|
|
{
|
|
kdtreeunserialize(s, &model->tree, _state);
|
|
processed = ae_true;
|
|
}
|
|
ae_assert(processed, "IDW: integrity check failed during serialization", _state);
|
|
|
|
/*
|
|
* Temporary buffers
|
|
*/
|
|
idwcreatecalcbuffer(model, &model->buffer, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function evaluates error metrics for the model using IDWTsCalcBuf()
|
|
to calculate model at each point.
|
|
|
|
NOTE: modern IDW algorithms (MSTAB, MSMOOTH) can generate residuals during
|
|
model construction, so they do not need this function in order to
|
|
evaluate error metrics.
|
|
|
|
Following fields of Rep are filled:
|
|
* rep.rmserror
|
|
* rep.avgerror
|
|
* rep.maxerror
|
|
* rep.r2
|
|
|
|
-- ALGLIB --
|
|
Copyright 22.10.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void idw_errormetricsviacalc(idwbuilder* state,
|
|
idwmodel* model,
|
|
idwreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t npoints;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double v;
|
|
double vv;
|
|
double rss;
|
|
double tss;
|
|
|
|
|
|
npoints = state->npoints;
|
|
nx = state->nx;
|
|
ny = state->ny;
|
|
if( npoints==0 )
|
|
{
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rep->r2 = (double)(1);
|
|
return;
|
|
}
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rss = (double)(0);
|
|
tss = (double)(0);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
model->buffer.x.ptr.p_double[j] = state->xy.ptr.p_double[i*(nx+ny)+j];
|
|
}
|
|
idwtscalcbuf(model, &model->buffer, &model->buffer.x, &model->buffer.y, _state);
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
vv = state->xy.ptr.p_double[i*(nx+ny)+nx+j];
|
|
v = ae_fabs(vv-model->buffer.y.ptr.p_double[j], _state);
|
|
rep->rmserror = rep->rmserror+v*v;
|
|
rep->avgerror = rep->avgerror+v;
|
|
rep->maxerror = ae_maxreal(rep->maxerror, v, _state);
|
|
rss = rss+v*v;
|
|
tss = tss+ae_sqr(vv-state->tmpmean.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/(npoints*ny), _state);
|
|
rep->avgerror = rep->avgerror/(npoints*ny);
|
|
rep->r2 = 1.0-rss/coalesce(tss, 1.0, _state);
|
|
}
|
|
|
|
|
|
void _idwcalcbuffer_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwcalcbuffer *p = (idwcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tsyw, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tsw, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->tsxy, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tsdist, 0, DT_REAL, _state, make_automatic);
|
|
_kdtreerequestbuffer_init(&p->requestbuffer, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _idwcalcbuffer_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwcalcbuffer *dst = (idwcalcbuffer*)_dst;
|
|
idwcalcbuffer *src = (idwcalcbuffer*)_src;
|
|
ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tsyw, &src->tsyw, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tsw, &src->tsw, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->tsxy, &src->tsxy, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tsdist, &src->tsdist, _state, make_automatic);
|
|
_kdtreerequestbuffer_init_copy(&dst->requestbuffer, &src->requestbuffer, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _idwcalcbuffer_clear(void* _p)
|
|
{
|
|
idwcalcbuffer *p = (idwcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->x);
|
|
ae_vector_clear(&p->y);
|
|
ae_vector_clear(&p->tsyw);
|
|
ae_vector_clear(&p->tsw);
|
|
ae_matrix_clear(&p->tsxy);
|
|
ae_vector_clear(&p->tsdist);
|
|
_kdtreerequestbuffer_clear(&p->requestbuffer);
|
|
}
|
|
|
|
|
|
void _idwcalcbuffer_destroy(void* _p)
|
|
{
|
|
idwcalcbuffer *p = (idwcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->x);
|
|
ae_vector_destroy(&p->y);
|
|
ae_vector_destroy(&p->tsyw);
|
|
ae_vector_destroy(&p->tsw);
|
|
ae_matrix_destroy(&p->tsxy);
|
|
ae_vector_destroy(&p->tsdist);
|
|
_kdtreerequestbuffer_destroy(&p->requestbuffer);
|
|
}
|
|
|
|
|
|
void _idwmodel_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwmodel *p = (idwmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->globalprior, 0, DT_REAL, _state, make_automatic);
|
|
_kdtree_init(&p->tree, _state, make_automatic);
|
|
ae_vector_init(&p->shepardxy, 0, DT_REAL, _state, make_automatic);
|
|
_idwcalcbuffer_init(&p->buffer, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _idwmodel_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwmodel *dst = (idwmodel*)_dst;
|
|
idwmodel *src = (idwmodel*)_src;
|
|
dst->nx = src->nx;
|
|
dst->ny = src->ny;
|
|
ae_vector_init_copy(&dst->globalprior, &src->globalprior, _state, make_automatic);
|
|
dst->algotype = src->algotype;
|
|
dst->nlayers = src->nlayers;
|
|
dst->r0 = src->r0;
|
|
dst->rdecay = src->rdecay;
|
|
dst->lambda0 = src->lambda0;
|
|
dst->lambdalast = src->lambdalast;
|
|
dst->lambdadecay = src->lambdadecay;
|
|
dst->shepardp = src->shepardp;
|
|
_kdtree_init_copy(&dst->tree, &src->tree, _state, make_automatic);
|
|
dst->npoints = src->npoints;
|
|
ae_vector_init_copy(&dst->shepardxy, &src->shepardxy, _state, make_automatic);
|
|
_idwcalcbuffer_init_copy(&dst->buffer, &src->buffer, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _idwmodel_clear(void* _p)
|
|
{
|
|
idwmodel *p = (idwmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->globalprior);
|
|
_kdtree_clear(&p->tree);
|
|
ae_vector_clear(&p->shepardxy);
|
|
_idwcalcbuffer_clear(&p->buffer);
|
|
}
|
|
|
|
|
|
void _idwmodel_destroy(void* _p)
|
|
{
|
|
idwmodel *p = (idwmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->globalprior);
|
|
_kdtree_destroy(&p->tree);
|
|
ae_vector_destroy(&p->shepardxy);
|
|
_idwcalcbuffer_destroy(&p->buffer);
|
|
}
|
|
|
|
|
|
void _idwbuilder_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwbuilder *p = (idwbuilder*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->priortermval, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->xy, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->tmpxy, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->tmplayers, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmptags, 0, DT_INT, _state, make_automatic);
|
|
ae_vector_init(&p->tmpdist, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmpx, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmpwy, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmpw, 0, DT_REAL, _state, make_automatic);
|
|
_kdtree_init(&p->tmptree, _state, make_automatic);
|
|
ae_vector_init(&p->tmpmean, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _idwbuilder_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwbuilder *dst = (idwbuilder*)_dst;
|
|
idwbuilder *src = (idwbuilder*)_src;
|
|
dst->priortermtype = src->priortermtype;
|
|
ae_vector_init_copy(&dst->priortermval, &src->priortermval, _state, make_automatic);
|
|
dst->algotype = src->algotype;
|
|
dst->nlayers = src->nlayers;
|
|
dst->r0 = src->r0;
|
|
dst->rdecay = src->rdecay;
|
|
dst->lambda0 = src->lambda0;
|
|
dst->lambdalast = src->lambdalast;
|
|
dst->lambdadecay = src->lambdadecay;
|
|
dst->shepardp = src->shepardp;
|
|
ae_vector_init_copy(&dst->xy, &src->xy, _state, make_automatic);
|
|
dst->npoints = src->npoints;
|
|
dst->nx = src->nx;
|
|
dst->ny = src->ny;
|
|
ae_matrix_init_copy(&dst->tmpxy, &src->tmpxy, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->tmplayers, &src->tmplayers, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmptags, &src->tmptags, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpdist, &src->tmpdist, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpx, &src->tmpx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpwy, &src->tmpwy, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpw, &src->tmpw, _state, make_automatic);
|
|
_kdtree_init_copy(&dst->tmptree, &src->tmptree, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpmean, &src->tmpmean, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _idwbuilder_clear(void* _p)
|
|
{
|
|
idwbuilder *p = (idwbuilder*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->priortermval);
|
|
ae_vector_clear(&p->xy);
|
|
ae_matrix_clear(&p->tmpxy);
|
|
ae_matrix_clear(&p->tmplayers);
|
|
ae_vector_clear(&p->tmptags);
|
|
ae_vector_clear(&p->tmpdist);
|
|
ae_vector_clear(&p->tmpx);
|
|
ae_vector_clear(&p->tmpwy);
|
|
ae_vector_clear(&p->tmpw);
|
|
_kdtree_clear(&p->tmptree);
|
|
ae_vector_clear(&p->tmpmean);
|
|
}
|
|
|
|
|
|
void _idwbuilder_destroy(void* _p)
|
|
{
|
|
idwbuilder *p = (idwbuilder*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->priortermval);
|
|
ae_vector_destroy(&p->xy);
|
|
ae_matrix_destroy(&p->tmpxy);
|
|
ae_matrix_destroy(&p->tmplayers);
|
|
ae_vector_destroy(&p->tmptags);
|
|
ae_vector_destroy(&p->tmpdist);
|
|
ae_vector_destroy(&p->tmpx);
|
|
ae_vector_destroy(&p->tmpwy);
|
|
ae_vector_destroy(&p->tmpw);
|
|
_kdtree_destroy(&p->tmptree);
|
|
ae_vector_destroy(&p->tmpmean);
|
|
}
|
|
|
|
|
|
void _idwreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwreport *p = (idwreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _idwreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
idwreport *dst = (idwreport*)_dst;
|
|
idwreport *src = (idwreport*)_src;
|
|
dst->rmserror = src->rmserror;
|
|
dst->avgerror = src->avgerror;
|
|
dst->maxerror = src->maxerror;
|
|
dst->r2 = src->r2;
|
|
}
|
|
|
|
|
|
void _idwreport_clear(void* _p)
|
|
{
|
|
idwreport *p = (idwreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _idwreport_destroy(void* _p)
|
|
{
|
|
idwreport *p = (idwreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RATINT) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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 = (double)(0);
|
|
s2 = (double)(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 = (double)(0);
|
|
return;
|
|
}
|
|
if( ae_fp_eq(b->sy,(double)(0)) )
|
|
{
|
|
*f = (double)(0);
|
|
*df = (double)(0);
|
|
return;
|
|
}
|
|
ae_assert(ae_fp_greater(b->sy,(double)(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 = (double)(2);
|
|
xoffs2 = (double)(-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 = (double)(0);
|
|
n1 = (double)(0);
|
|
d0 = (double)(0);
|
|
d1 = (double)(0);
|
|
xprev = (double)(-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 = (double)(1);
|
|
s1 = (double)(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,(double)(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 = (double)(0);
|
|
*d2f = (double)(0);
|
|
return;
|
|
}
|
|
if( ae_fp_eq(b->sy,(double)(0)) )
|
|
{
|
|
*f = (double)(0);
|
|
*df = (double)(0);
|
|
*d2f = (double)(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,(double)(0)), "BarycentricDiff: internal error", _state);
|
|
*f = (double)(0);
|
|
*df = (double)(0);
|
|
*d2f = (double)(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 = (double)(0);
|
|
n1 = (double)(0);
|
|
n2 = (double)(0);
|
|
d0 = (double)(0);
|
|
d1 = (double)(0);
|
|
d2 = (double)(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 = (double)(1);
|
|
s1 = (double)(0);
|
|
s2 = (double)(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,(double)(0)) )
|
|
{
|
|
b->sy = barycentriccalc(b, cb, _state);
|
|
v = (double)(1);
|
|
for(i=0; i<=b->n-1; i++)
|
|
{
|
|
b->y.ptr.p_double[i] = (double)(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,(double)(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 = (double)(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,(double)(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);
|
|
memset(&perm, 0, sizeof(perm));
|
|
memset(&wtemp, 0, sizeof(wtemp));
|
|
memset(&sortrbuf, 0, sizeof(sortrbuf));
|
|
memset(&sortrbuf2, 0, sizeof(sortrbuf2));
|
|
_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] = (double)(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 = (double)(1);
|
|
for(k=1; k<=d; k++)
|
|
{
|
|
s0 = -s0;
|
|
}
|
|
for(k=0; k<=n-1; k++)
|
|
{
|
|
|
|
/*
|
|
* Wk
|
|
*/
|
|
s = (double)(0);
|
|
for(i=ae_maxint(k-d, 0, _state); i<=ae_minint(k, n-1-d, _state); i++)
|
|
{
|
|
v = (double)(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);
|
|
memset(&p1, 0, sizeof(p1));
|
|
memset(&p2, 0, sizeof(p2));
|
|
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 = (double)(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,(double)(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 = (double)(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,(double)(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);
|
|
}
|
|
|
|
|
|
void _barycentricinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
barycentricinterpolant *p = (barycentricinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->w, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _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;
|
|
ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->w, &src->w, _state, make_automatic);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_FITSPHERE) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
Fits least squares (LS) circle (or NX-dimensional sphere) to data (a set
|
|
of points in NX-dimensional space).
|
|
|
|
Least squares circle minimizes sum of squared deviations between distances
|
|
from points to the center and some "candidate" radius, which is also
|
|
fitted to the data.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
R - radius
|
|
|
|
-- ALGLIB --
|
|
Copyright 07.05.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspherels(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
/* Real */ ae_vector* cx,
|
|
double* r,
|
|
ae_state *_state)
|
|
{
|
|
double dummy;
|
|
|
|
ae_vector_clear(cx);
|
|
*r = 0;
|
|
|
|
fitspherex(xy, npoints, nx, 0, 0.0, 0, 0.0, cx, &dummy, r, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fits minimum circumscribed (MC) circle (or NX-dimensional sphere) to data
|
|
(a set of points in NX-dimensional space).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RHi - radius
|
|
|
|
NOTE: this function is an easy-to-use wrapper around more powerful "expert"
|
|
function fitspherex().
|
|
|
|
This wrapper is optimized for ease of use and stability - at the
|
|
cost of somewhat lower performance (we have to use very tight
|
|
stopping criteria for inner optimizer because we want to make sure
|
|
that it will converge on any dataset).
|
|
|
|
If you are ready to experiment with settings of "expert" function,
|
|
you can achieve ~2-4x speedup over standard "bulletproof" settings.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspheremc(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
/* Real */ ae_vector* cx,
|
|
double* rhi,
|
|
ae_state *_state)
|
|
{
|
|
double dummy;
|
|
|
|
ae_vector_clear(cx);
|
|
*rhi = 0;
|
|
|
|
fitspherex(xy, npoints, nx, 1, 0.0, 0, 0.0, cx, &dummy, rhi, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fits maximum inscribed circle (or NX-dimensional sphere) to data (a set of
|
|
points in NX-dimensional space).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RLo - radius
|
|
|
|
NOTE: this function is an easy-to-use wrapper around more powerful "expert"
|
|
function fitspherex().
|
|
|
|
This wrapper is optimized for ease of use and stability - at the
|
|
cost of somewhat lower performance (we have to use very tight
|
|
stopping criteria for inner optimizer because we want to make sure
|
|
that it will converge on any dataset).
|
|
|
|
If you are ready to experiment with settings of "expert" function,
|
|
you can achieve ~2-4x speedup over standard "bulletproof" settings.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspheremi(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
/* Real */ ae_vector* cx,
|
|
double* rlo,
|
|
ae_state *_state)
|
|
{
|
|
double dummy;
|
|
|
|
ae_vector_clear(cx);
|
|
*rlo = 0;
|
|
|
|
fitspherex(xy, npoints, nx, 2, 0.0, 0, 0.0, cx, rlo, &dummy, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fits minimum zone circle (or NX-dimensional sphere) to data (a set of
|
|
points in NX-dimensional space).
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RLo - radius of inscribed circle
|
|
RHo - radius of circumscribed circle
|
|
|
|
NOTE: this function is an easy-to-use wrapper around more powerful "expert"
|
|
function fitspherex().
|
|
|
|
This wrapper is optimized for ease of use and stability - at the
|
|
cost of somewhat lower performance (we have to use very tight
|
|
stopping criteria for inner optimizer because we want to make sure
|
|
that it will converge on any dataset).
|
|
|
|
If you are ready to experiment with settings of "expert" function,
|
|
you can achieve ~2-4x speedup over standard "bulletproof" settings.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspheremz(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
/* Real */ ae_vector* cx,
|
|
double* rlo,
|
|
double* rhi,
|
|
ae_state *_state)
|
|
{
|
|
|
|
ae_vector_clear(cx);
|
|
*rlo = 0;
|
|
*rhi = 0;
|
|
|
|
fitspherex(xy, npoints, nx, 3, 0.0, 0, 0.0, cx, rlo, rhi, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fitting minimum circumscribed, maximum inscribed or minimum zone circles
|
|
(or NX-dimensional spheres) to data (a set of points in NX-dimensional
|
|
space).
|
|
|
|
This is expert function which allows to tweak many parameters of
|
|
underlying nonlinear solver:
|
|
* stopping criteria for inner iterations
|
|
* number of outer iterations
|
|
* penalty coefficient used to handle nonlinear constraints (we convert
|
|
unconstrained nonsmooth optimization problem ivolving max() and/or min()
|
|
operations to quadratically constrained smooth one).
|
|
|
|
You may tweak all these parameters or only some of them, leaving other
|
|
ones at their default state - just specify zero value, and solver will
|
|
fill it with appropriate default one.
|
|
|
|
These comments also include some discussion of approach used to handle
|
|
such unusual fitting problem, its stability, drawbacks of alternative
|
|
methods, and convergence properties.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
ProblemType-used to encode problem type:
|
|
* 0 for least squares circle
|
|
* 1 for minimum circumscribed circle/sphere fitting (MC)
|
|
* 2 for maximum inscribed circle/sphere fitting (MI)
|
|
* 3 for minimum zone circle fitting (difference between
|
|
Rhi and Rlo is minimized), denoted as MZ
|
|
EpsX - stopping condition for NLC optimizer:
|
|
* must be non-negative
|
|
* use 0 to choose default value (1.0E-12 is used by default)
|
|
* you may specify larger values, up to 1.0E-6, if you want
|
|
to speed-up solver; NLC solver performs several
|
|
preconditioned outer iterations, so final result
|
|
typically has precision much better than EpsX.
|
|
AULIts - number of outer iterations performed by NLC optimizer:
|
|
* must be non-negative
|
|
* use 0 to choose default value (20 is used by default)
|
|
* you may specify values smaller than 20 if you want to
|
|
speed up solver; 10 often results in good combination of
|
|
precision and speed; sometimes you may get good results
|
|
with just 6 outer iterations.
|
|
Ignored for ProblemType=0.
|
|
Penalty - penalty coefficient for NLC optimizer:
|
|
* must be non-negative
|
|
* use 0 to choose default value (1.0E6 in current version)
|
|
* it should be really large, 1.0E6...1.0E7 is a good value
|
|
to start from;
|
|
* generally, default value is good enough
|
|
Ignored for ProblemType=0.
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RLo - radius:
|
|
* for ProblemType=2,3, radius of the inscribed sphere
|
|
* for ProblemType=0 - radius of the least squares sphere
|
|
* for ProblemType=1 - zero
|
|
RHo - radius:
|
|
* for ProblemType=1,3, radius of the circumscribed sphere
|
|
* for ProblemType=0 - radius of the least squares sphere
|
|
* for ProblemType=2 - zero
|
|
|
|
NOTE: ON THE UNIQUENESS OF SOLUTIONS
|
|
|
|
ALGLIB provides solution to several related circle fitting problems: MC
|
|
(minimum circumscribed), MI (maximum inscribed) and MZ (minimum zone)
|
|
fitting, LS (least squares) fitting.
|
|
|
|
It is important to note that among these problems only MC and LS are
|
|
convex and have unique solution independently from starting point.
|
|
|
|
As for MI, it may (or may not, depending on dataset properties) have
|
|
multiple solutions, and it always has one degenerate solution C=infinity
|
|
which corresponds to infinitely large radius. Thus, there are no guarantees
|
|
that solution to MI returned by this solver will be the best one (and no
|
|
one can provide you with such guarantee because problem is NP-hard). The
|
|
only guarantee you have is that this solution is locally optimal, i.e. it
|
|
can not be improved by infinitesimally small tweaks in the parameters.
|
|
|
|
It is also possible to "run away" to infinity when started from bad
|
|
initial point located outside of point cloud (or when point cloud does not
|
|
span entire circumference/surface of the sphere).
|
|
|
|
Finally, MZ (minimum zone circle) stands somewhere between MC and MI in
|
|
stability. It is somewhat regularized by "circumscribed" term of the merit
|
|
function; however, solutions to MZ may be non-unique, and in some unlucky
|
|
cases it is also possible to "run away to infinity".
|
|
|
|
|
|
NOTE: ON THE NONLINEARLY CONSTRAINED PROGRAMMING APPROACH
|
|
|
|
The problem formulation for MC (minimum circumscribed circle; for the
|
|
sake of simplicity we omit MZ and MI here) is:
|
|
|
|
[ [ ]2 ]
|
|
min [ max [ XY[i]-C ] ]
|
|
C [ i [ ] ]
|
|
|
|
i.e. it is unconstrained nonsmooth optimization problem of finding "best"
|
|
central point, with radius R being unambiguously determined from C. In
|
|
order to move away from non-smoothness we use following reformulation:
|
|
|
|
[ ] [ ]2
|
|
min [ R ] subject to R>=0, [ XY[i]-C ] <= R^2
|
|
C,R [ ] [ ]
|
|
|
|
i.e. it becomes smooth quadratically constrained optimization problem with
|
|
linear target function. Such problem statement is 100% equivalent to the
|
|
original nonsmooth one, but much easier to approach. We solve it with
|
|
MinNLC solver provided by ALGLIB.
|
|
|
|
|
|
NOTE: ON INSTABILITY OF SEQUENTIAL LINEARIZATION APPROACH
|
|
|
|
ALGLIB has nonlinearly constrained solver which proved to be stable on
|
|
such problems. However, some authors proposed to linearize constraints in
|
|
the vicinity of current approximation (Ci,Ri) and to get next approximate
|
|
solution (Ci+1,Ri+1) as solution to linear programming problem. Obviously,
|
|
LP problems are easier than nonlinearly constrained ones.
|
|
|
|
Indeed, such approach to MC/MI/MZ resulted in ~10-20x increase in
|
|
performance (when compared with NLC solver). However, it turned out that
|
|
in some cases linearized model fails to predict correct direction for next
|
|
step and tells us that we converged to solution even when we are still 2-4
|
|
digits of precision away from it.
|
|
|
|
It is important that it is not failure of LP solver - it is failure of the
|
|
linear model; even when solved exactly, it fails to handle subtle
|
|
nonlinearities which arise near the solution. We validated it by comparing
|
|
results returned by ALGLIB linear solver with that of MATLAB.
|
|
|
|
In our experiments with linearization:
|
|
* MC failed most often, at both realistic and synthetic datasets
|
|
* MI sometimes failed, but sometimes succeeded
|
|
* MZ often succeeded; our guess is that presence of two independent sets
|
|
of constraints (one set for Rlo and another one for Rhi) and two terms
|
|
in the target function (Rlo and Rhi) regularizes task, so when linear
|
|
model fails to handle nonlinearities from Rlo, it uses Rhi as a hint
|
|
(and vice versa).
|
|
|
|
Because linearization approach failed to achieve stable results, we do not
|
|
include it in ALGLIB.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitspherex(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
ae_int_t problemtype,
|
|
double epsx,
|
|
ae_int_t aulits,
|
|
double penalty,
|
|
/* Real */ ae_vector* cx,
|
|
double* rlo,
|
|
double* rhi,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
fitsphereinternalreport rep;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&rep, 0, sizeof(rep));
|
|
ae_vector_clear(cx);
|
|
*rlo = 0;
|
|
*rhi = 0;
|
|
_fitsphereinternalreport_init(&rep, _state, ae_true);
|
|
|
|
ae_assert(ae_isfinite(penalty, _state)&&ae_fp_greater_eq(penalty,(double)(0)), "FitSphereX: Penalty<0 or is not finite", _state);
|
|
ae_assert(ae_isfinite(epsx, _state)&&ae_fp_greater_eq(epsx,(double)(0)), "FitSphereX: EpsX<0 or is not finite", _state);
|
|
ae_assert(aulits>=0, "FitSphereX: AULIts<0", _state);
|
|
fitsphereinternal(xy, npoints, nx, problemtype, 0, epsx, aulits, penalty, cx, rlo, rhi, &rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fitting minimum circumscribed, maximum inscribed or minimum zone circles
|
|
(or NX-dimensional spheres) to data (a set of points in NX-dimensional
|
|
space).
|
|
|
|
Internal computational function.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints,NX] (or larger), contains dataset.
|
|
One row = one point in NX-dimensional space.
|
|
NPoints - dataset size, NPoints>0
|
|
NX - space dimensionality, NX>0 (1, 2, 3, 4, 5 and so on)
|
|
ProblemType-used to encode problem type:
|
|
* 0 for least squares circle
|
|
* 1 for minimum circumscribed circle/sphere fitting (MC)
|
|
* 2 for maximum inscribed circle/sphere fitting (MI)
|
|
* 3 for minimum zone circle fitting (difference between
|
|
Rhi and Rlo is minimized), denoted as MZ
|
|
SolverType- solver to use:
|
|
* 0 use best solver available (1 in current version)
|
|
* 1 use nonlinearly constrained optimization approach, AUL
|
|
(it is roughly 10-20 times slower than SPC-LIN, but
|
|
much more stable)
|
|
* 2 use special fast IMPRECISE solver, SPC-LIN sequential
|
|
linearization approach; SPC-LIN is fast, but sometimes
|
|
fails to converge with more than 3 digits of precision;
|
|
see comments below.
|
|
NOT RECOMMENDED UNLESS YOU REALLY NEED HIGH PERFORMANCE
|
|
AT THE COST OF SOME PRECISION.
|
|
* 3 use nonlinearly constrained optimization approach, SLP
|
|
(most robust one, but somewhat slower than AUL)
|
|
Ignored for ProblemType=0.
|
|
EpsX - stopping criteria for SLP and NLC optimizers:
|
|
* must be non-negative
|
|
* use 0 to choose default value (1.0E-12 is used by default)
|
|
* if you use SLP solver, you should use default values
|
|
* if you use NLC solver, you may specify larger values, up
|
|
to 1.0E-6, if you want to speed-up solver; NLC solver
|
|
performs several preconditioned outer iterations, so final
|
|
result typically has precision much better than EpsX.
|
|
AULIts - number of iterations performed by NLC optimizer:
|
|
* must be non-negative
|
|
* use 0 to choose default value (20 is used by default)
|
|
* you may specify values smaller than 20 if you want to
|
|
speed up solver; 10 often results in good combination of
|
|
precision and speed
|
|
Ignored for ProblemType=0.
|
|
Penalty - penalty coefficient for NLC optimizer (ignored for SLP):
|
|
* must be non-negative
|
|
* use 0 to choose default value (1.0E6 in current version)
|
|
* it should be really large, 1.0E6...1.0E7 is a good value
|
|
to start from;
|
|
* generally, default value is good enough
|
|
* ignored by SLP optimizer
|
|
Ignored for ProblemType=0.
|
|
|
|
OUTPUT PARAMETERS:
|
|
CX - central point for a sphere
|
|
RLo - radius:
|
|
* for ProblemType=2,3, radius of the inscribed sphere
|
|
* for ProblemType=0 - radius of the least squares sphere
|
|
* for ProblemType=1 - zero
|
|
RHo - radius:
|
|
* for ProblemType=1,3, radius of the circumscribed sphere
|
|
* for ProblemType=0 - radius of the least squares sphere
|
|
* for ProblemType=2 - zero
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void fitsphereinternal(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
ae_int_t problemtype,
|
|
ae_int_t solvertype,
|
|
double epsx,
|
|
ae_int_t aulits,
|
|
double penalty,
|
|
/* Real */ ae_vector* cx,
|
|
double* rlo,
|
|
double* rhi,
|
|
fitsphereinternalreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double v;
|
|
double vv;
|
|
ae_int_t cpr;
|
|
ae_bool userlo;
|
|
ae_bool userhi;
|
|
double vlo;
|
|
double vhi;
|
|
ae_vector vmin;
|
|
ae_vector vmax;
|
|
double spread;
|
|
ae_vector pcr;
|
|
ae_vector scr;
|
|
ae_vector bl;
|
|
ae_vector bu;
|
|
ae_int_t suboffset;
|
|
ae_int_t dstrow;
|
|
minnlcstate nlcstate;
|
|
minnlcreport nlcrep;
|
|
ae_matrix cmatrix;
|
|
ae_vector ct;
|
|
ae_int_t outeridx;
|
|
ae_int_t maxouterits;
|
|
ae_int_t maxits;
|
|
double safeguard;
|
|
double bi;
|
|
minbleicstate blcstate;
|
|
minbleicreport blcrep;
|
|
ae_vector prevc;
|
|
minlmstate lmstate;
|
|
minlmreport lmrep;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&vmin, 0, sizeof(vmin));
|
|
memset(&vmax, 0, sizeof(vmax));
|
|
memset(&pcr, 0, sizeof(pcr));
|
|
memset(&scr, 0, sizeof(scr));
|
|
memset(&bl, 0, sizeof(bl));
|
|
memset(&bu, 0, sizeof(bu));
|
|
memset(&nlcstate, 0, sizeof(nlcstate));
|
|
memset(&nlcrep, 0, sizeof(nlcrep));
|
|
memset(&cmatrix, 0, sizeof(cmatrix));
|
|
memset(&ct, 0, sizeof(ct));
|
|
memset(&blcstate, 0, sizeof(blcstate));
|
|
memset(&blcrep, 0, sizeof(blcrep));
|
|
memset(&prevc, 0, sizeof(prevc));
|
|
memset(&lmstate, 0, sizeof(lmstate));
|
|
memset(&lmrep, 0, sizeof(lmrep));
|
|
ae_vector_clear(cx);
|
|
*rlo = 0;
|
|
*rhi = 0;
|
|
_fitsphereinternalreport_clear(rep);
|
|
ae_vector_init(&vmin, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&vmax, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&pcr, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&scr, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bl, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bu, 0, DT_REAL, _state, ae_true);
|
|
_minnlcstate_init(&nlcstate, _state, ae_true);
|
|
_minnlcreport_init(&nlcrep, _state, ae_true);
|
|
ae_matrix_init(&cmatrix, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ct, 0, DT_INT, _state, ae_true);
|
|
_minbleicstate_init(&blcstate, _state, ae_true);
|
|
_minbleicreport_init(&blcrep, _state, ae_true);
|
|
ae_vector_init(&prevc, 0, DT_REAL, _state, ae_true);
|
|
_minlmstate_init(&lmstate, _state, ae_true);
|
|
_minlmreport_init(&lmrep, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Check input parameters
|
|
*/
|
|
ae_assert(npoints>0, "FitSphereX: NPoints<=0", _state);
|
|
ae_assert(nx>0, "FitSphereX: NX<=0", _state);
|
|
ae_assert(apservisfinitematrix(xy, npoints, nx, _state), "FitSphereX: XY contains infinite or NAN values", _state);
|
|
ae_assert(problemtype>=0&&problemtype<=3, "FitSphereX: ProblemType is neither 0, 1, 2 or 3", _state);
|
|
ae_assert(solvertype>=0&&solvertype<=3, "FitSphereX: ProblemType is neither 1, 2 or 3", _state);
|
|
ae_assert(ae_isfinite(penalty, _state)&&ae_fp_greater_eq(penalty,(double)(0)), "FitSphereX: Penalty<0 or is not finite", _state);
|
|
ae_assert(ae_isfinite(epsx, _state)&&ae_fp_greater_eq(epsx,(double)(0)), "FitSphereX: EpsX<0 or is not finite", _state);
|
|
ae_assert(aulits>=0, "FitSphereX: AULIts<0", _state);
|
|
if( solvertype==0 )
|
|
{
|
|
solvertype = 1;
|
|
}
|
|
if( ae_fp_eq(penalty,(double)(0)) )
|
|
{
|
|
penalty = 1.0E6;
|
|
}
|
|
if( ae_fp_eq(epsx,(double)(0)) )
|
|
{
|
|
epsx = 1.0E-12;
|
|
}
|
|
if( aulits==0 )
|
|
{
|
|
aulits = 20;
|
|
}
|
|
safeguard = (double)(10);
|
|
maxouterits = 10;
|
|
maxits = 10000;
|
|
rep->nfev = 0;
|
|
rep->iterationscount = 0;
|
|
|
|
/*
|
|
* Determine initial values, initial estimates and spread of the points
|
|
*/
|
|
ae_vector_set_length(&vmin, nx, _state);
|
|
ae_vector_set_length(&vmax, nx, _state);
|
|
ae_vector_set_length(cx, nx, _state);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
vmin.ptr.p_double[j] = xy->ptr.pp_double[0][j];
|
|
vmax.ptr.p_double[j] = xy->ptr.pp_double[0][j];
|
|
cx->ptr.p_double[j] = (double)(0);
|
|
}
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cx->ptr.p_double[j] = cx->ptr.p_double[j]+xy->ptr.pp_double[i][j];
|
|
vmin.ptr.p_double[j] = ae_minreal(vmin.ptr.p_double[j], xy->ptr.pp_double[i][j], _state);
|
|
vmax.ptr.p_double[j] = ae_maxreal(vmax.ptr.p_double[j], xy->ptr.pp_double[i][j], _state);
|
|
}
|
|
}
|
|
spread = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cx->ptr.p_double[j] = cx->ptr.p_double[j]/npoints;
|
|
spread = ae_maxreal(spread, vmax.ptr.p_double[j]-vmin.ptr.p_double[j], _state);
|
|
}
|
|
*rlo = ae_maxrealnumber;
|
|
*rhi = (double)(0);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(xy->ptr.pp_double[i][j]-cx->ptr.p_double[j], _state);
|
|
}
|
|
v = ae_sqrt(v, _state);
|
|
*rhi = ae_maxreal(*rhi, v, _state);
|
|
*rlo = ae_minreal(*rlo, v, _state);
|
|
}
|
|
|
|
/*
|
|
* Handle degenerate case of zero spread
|
|
*/
|
|
if( ae_fp_eq(spread,(double)(0)) )
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cx->ptr.p_double[j] = vmin.ptr.p_double[j];
|
|
}
|
|
*rhi = (double)(0);
|
|
*rlo = (double)(0);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Prepare initial point for optimizer, scale vector and box constraints
|
|
*/
|
|
ae_vector_set_length(&pcr, nx+2, _state);
|
|
ae_vector_set_length(&scr, nx+2, _state);
|
|
ae_vector_set_length(&bl, nx+2, _state);
|
|
ae_vector_set_length(&bu, nx+2, _state);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
pcr.ptr.p_double[j] = cx->ptr.p_double[j];
|
|
scr.ptr.p_double[j] = 0.1*spread;
|
|
bl.ptr.p_double[j] = cx->ptr.p_double[j]-safeguard*spread;
|
|
bu.ptr.p_double[j] = cx->ptr.p_double[j]+safeguard*spread;
|
|
}
|
|
pcr.ptr.p_double[nx+0] = *rlo;
|
|
pcr.ptr.p_double[nx+1] = *rhi;
|
|
scr.ptr.p_double[nx+0] = 0.5*spread;
|
|
scr.ptr.p_double[nx+1] = 0.5*spread;
|
|
bl.ptr.p_double[nx+0] = (double)(0);
|
|
bl.ptr.p_double[nx+1] = (double)(0);
|
|
bu.ptr.p_double[nx+0] = safeguard*(*rhi);
|
|
bu.ptr.p_double[nx+1] = safeguard*(*rhi);
|
|
|
|
/*
|
|
* First branch: least squares fitting vs MI/MC/MZ fitting
|
|
*/
|
|
if( problemtype==0 )
|
|
{
|
|
|
|
/*
|
|
* Solve problem with Levenberg-Marquardt algorithm
|
|
*/
|
|
pcr.ptr.p_double[nx] = *rhi;
|
|
minlmcreatevj(nx+1, npoints, &pcr, &lmstate, _state);
|
|
minlmsetscale(&lmstate, &scr, _state);
|
|
minlmsetbc(&lmstate, &bl, &bu, _state);
|
|
minlmsetcond(&lmstate, epsx, maxits, _state);
|
|
while(minlmiteration(&lmstate, _state))
|
|
{
|
|
if( lmstate.needfij||lmstate.needfi )
|
|
{
|
|
inc(&rep->nfev, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(lmstate.x.ptr.p_double[j]-xy->ptr.pp_double[i][j], _state);
|
|
}
|
|
lmstate.fi.ptr.p_double[i] = ae_sqrt(v, _state)-lmstate.x.ptr.p_double[nx];
|
|
if( lmstate.needfij )
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
lmstate.j.ptr.pp_double[i][j] = 0.5/(1.0E-9*spread+ae_sqrt(v, _state))*2*(lmstate.x.ptr.p_double[j]-xy->ptr.pp_double[i][j]);
|
|
}
|
|
lmstate.j.ptr.pp_double[i][nx] = (double)(-1);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
ae_assert(ae_false, "Assertion failed", _state);
|
|
}
|
|
minlmresults(&lmstate, &pcr, &lmrep, _state);
|
|
ae_assert(lmrep.terminationtype>0, "FitSphereX: unexpected failure of LM solver", _state);
|
|
rep->iterationscount = rep->iterationscount+lmrep.iterationscount;
|
|
|
|
/*
|
|
* Offload center coordinates from PCR to CX,
|
|
* re-calculate exact value of RLo/RHi using CX.
|
|
*/
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cx->ptr.p_double[j] = pcr.ptr.p_double[j];
|
|
}
|
|
vv = (double)(0);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(xy->ptr.pp_double[i][j]-cx->ptr.p_double[j], _state);
|
|
}
|
|
v = ae_sqrt(v, _state);
|
|
vv = vv+v/npoints;
|
|
}
|
|
*rlo = vv;
|
|
*rhi = vv;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* MI, MC, MZ fitting.
|
|
* Prepare problem metrics
|
|
*/
|
|
userlo = problemtype==2||problemtype==3;
|
|
userhi = problemtype==1||problemtype==3;
|
|
if( userlo&&userhi )
|
|
{
|
|
cpr = 2;
|
|
}
|
|
else
|
|
{
|
|
cpr = 1;
|
|
}
|
|
if( userlo )
|
|
{
|
|
vlo = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
vlo = (double)(0);
|
|
}
|
|
if( userhi )
|
|
{
|
|
vhi = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
vhi = (double)(0);
|
|
}
|
|
|
|
/*
|
|
* Solve with NLC solver; problem is treated as general nonlinearly constrained
|
|
* programming, with augmented Lagrangian solver or SLP being used.
|
|
*/
|
|
if( solvertype==1||solvertype==3 )
|
|
{
|
|
minnlccreate(nx+2, &pcr, &nlcstate, _state);
|
|
minnlcsetscale(&nlcstate, &scr, _state);
|
|
minnlcsetbc(&nlcstate, &bl, &bu, _state);
|
|
minnlcsetnlc(&nlcstate, 0, cpr*npoints, _state);
|
|
minnlcsetcond(&nlcstate, epsx, maxits, _state);
|
|
minnlcsetprecexactrobust(&nlcstate, 5, _state);
|
|
minnlcsetstpmax(&nlcstate, 0.1, _state);
|
|
if( solvertype==1 )
|
|
{
|
|
minnlcsetalgoaul(&nlcstate, penalty, aulits, _state);
|
|
}
|
|
else
|
|
{
|
|
minnlcsetalgoslp(&nlcstate, _state);
|
|
}
|
|
minnlcrestartfrom(&nlcstate, &pcr, _state);
|
|
while(minnlciteration(&nlcstate, _state))
|
|
{
|
|
if( nlcstate.needfij )
|
|
{
|
|
inc(&rep->nfev, _state);
|
|
nlcstate.fi.ptr.p_double[0] = vhi*nlcstate.x.ptr.p_double[nx+1]-vlo*nlcstate.x.ptr.p_double[nx+0];
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
nlcstate.j.ptr.pp_double[0][j] = (double)(0);
|
|
}
|
|
nlcstate.j.ptr.pp_double[0][nx+0] = -1*vlo;
|
|
nlcstate.j.ptr.pp_double[0][nx+1] = 1*vhi;
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
suboffset = 0;
|
|
if( userhi )
|
|
{
|
|
dstrow = 1+cpr*i+suboffset;
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
vv = nlcstate.x.ptr.p_double[j]-xy->ptr.pp_double[i][j];
|
|
v = v+vv*vv;
|
|
nlcstate.j.ptr.pp_double[dstrow][j] = 2*vv;
|
|
}
|
|
vv = nlcstate.x.ptr.p_double[nx+1];
|
|
v = v-vv*vv;
|
|
nlcstate.j.ptr.pp_double[dstrow][nx+0] = (double)(0);
|
|
nlcstate.j.ptr.pp_double[dstrow][nx+1] = -2*vv;
|
|
nlcstate.fi.ptr.p_double[dstrow] = v;
|
|
inc(&suboffset, _state);
|
|
}
|
|
if( userlo )
|
|
{
|
|
dstrow = 1+cpr*i+suboffset;
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
vv = nlcstate.x.ptr.p_double[j]-xy->ptr.pp_double[i][j];
|
|
v = v-vv*vv;
|
|
nlcstate.j.ptr.pp_double[dstrow][j] = -2*vv;
|
|
}
|
|
vv = nlcstate.x.ptr.p_double[nx+0];
|
|
v = v+vv*vv;
|
|
nlcstate.j.ptr.pp_double[dstrow][nx+0] = 2*vv;
|
|
nlcstate.j.ptr.pp_double[dstrow][nx+1] = (double)(0);
|
|
nlcstate.fi.ptr.p_double[dstrow] = v;
|
|
inc(&suboffset, _state);
|
|
}
|
|
ae_assert(suboffset==cpr, "Assertion failed", _state);
|
|
}
|
|
continue;
|
|
}
|
|
ae_assert(ae_false, "Assertion failed", _state);
|
|
}
|
|
minnlcresults(&nlcstate, &pcr, &nlcrep, _state);
|
|
ae_assert(nlcrep.terminationtype>0, "FitSphereX: unexpected failure of NLC solver", _state);
|
|
rep->iterationscount = rep->iterationscount+nlcrep.iterationscount;
|
|
|
|
/*
|
|
* Offload center coordinates from PCR to CX,
|
|
* re-calculate exact value of RLo/RHi using CX.
|
|
*/
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cx->ptr.p_double[j] = pcr.ptr.p_double[j];
|
|
}
|
|
*rlo = ae_maxrealnumber;
|
|
*rhi = (double)(0);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(xy->ptr.pp_double[i][j]-cx->ptr.p_double[j], _state);
|
|
}
|
|
v = ae_sqrt(v, _state);
|
|
*rhi = ae_maxreal(*rhi, v, _state);
|
|
*rlo = ae_minreal(*rlo, v, _state);
|
|
}
|
|
if( !userlo )
|
|
{
|
|
*rlo = (double)(0);
|
|
}
|
|
if( !userhi )
|
|
{
|
|
*rhi = (double)(0);
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Solve problem with SLP (sequential LP) approach; this approach
|
|
* is much faster than NLP, but often fails for MI and MC (for MZ
|
|
* it performs well enough).
|
|
*
|
|
* REFERENCE: "On a sequential linear programming approach to finding
|
|
* the smallest circumscribed, largest inscribed, and minimum
|
|
* zone circle or sphere", Helmuth Spath and G.A.Watson
|
|
*/
|
|
if( solvertype==2 )
|
|
{
|
|
ae_matrix_set_length(&cmatrix, cpr*npoints, nx+3, _state);
|
|
ae_vector_set_length(&ct, cpr*npoints, _state);
|
|
ae_vector_set_length(&prevc, nx, _state);
|
|
minbleiccreate(nx+2, &pcr, &blcstate, _state);
|
|
minbleicsetscale(&blcstate, &scr, _state);
|
|
minbleicsetbc(&blcstate, &bl, &bu, _state);
|
|
minbleicsetcond(&blcstate, (double)(0), (double)(0), epsx, maxits, _state);
|
|
for(outeridx=0; outeridx<=maxouterits-1; outeridx++)
|
|
{
|
|
|
|
/*
|
|
* Prepare initial point for algorithm; center coordinates at
|
|
* PCR are used to calculate RLo/RHi and update PCR with them.
|
|
*/
|
|
*rlo = ae_maxrealnumber;
|
|
*rhi = (double)(0);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(xy->ptr.pp_double[i][j]-pcr.ptr.p_double[j], _state);
|
|
}
|
|
v = ae_sqrt(v, _state);
|
|
*rhi = ae_maxreal(*rhi, v, _state);
|
|
*rlo = ae_minreal(*rlo, v, _state);
|
|
}
|
|
pcr.ptr.p_double[nx+0] = *rlo*0.99999;
|
|
pcr.ptr.p_double[nx+1] = *rhi/0.99999;
|
|
|
|
/*
|
|
* Generate matrix of linear constraints
|
|
*/
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(xy->ptr.pp_double[i][j], _state);
|
|
}
|
|
bi = -v/2;
|
|
suboffset = 0;
|
|
if( userhi )
|
|
{
|
|
dstrow = cpr*i+suboffset;
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cmatrix.ptr.pp_double[dstrow][j] = pcr.ptr.p_double[j]/2-xy->ptr.pp_double[i][j];
|
|
}
|
|
cmatrix.ptr.pp_double[dstrow][nx+0] = (double)(0);
|
|
cmatrix.ptr.pp_double[dstrow][nx+1] = -*rhi/2;
|
|
cmatrix.ptr.pp_double[dstrow][nx+2] = bi;
|
|
ct.ptr.p_int[dstrow] = -1;
|
|
inc(&suboffset, _state);
|
|
}
|
|
if( userlo )
|
|
{
|
|
dstrow = cpr*i+suboffset;
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cmatrix.ptr.pp_double[dstrow][j] = -(pcr.ptr.p_double[j]/2-xy->ptr.pp_double[i][j]);
|
|
}
|
|
cmatrix.ptr.pp_double[dstrow][nx+0] = *rlo/2;
|
|
cmatrix.ptr.pp_double[dstrow][nx+1] = (double)(0);
|
|
cmatrix.ptr.pp_double[dstrow][nx+2] = -bi;
|
|
ct.ptr.p_int[dstrow] = -1;
|
|
inc(&suboffset, _state);
|
|
}
|
|
ae_assert(suboffset==cpr, "Assertion failed", _state);
|
|
}
|
|
|
|
/*
|
|
* Solve LP subproblem with MinBLEIC
|
|
*/
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
prevc.ptr.p_double[j] = pcr.ptr.p_double[j];
|
|
}
|
|
minbleicsetlc(&blcstate, &cmatrix, &ct, cpr*npoints, _state);
|
|
minbleicrestartfrom(&blcstate, &pcr, _state);
|
|
while(minbleiciteration(&blcstate, _state))
|
|
{
|
|
if( blcstate.needfg )
|
|
{
|
|
inc(&rep->nfev, _state);
|
|
blcstate.f = vhi*blcstate.x.ptr.p_double[nx+1]-vlo*blcstate.x.ptr.p_double[nx+0];
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
blcstate.g.ptr.p_double[j] = (double)(0);
|
|
}
|
|
blcstate.g.ptr.p_double[nx+0] = -1*vlo;
|
|
blcstate.g.ptr.p_double[nx+1] = 1*vhi;
|
|
continue;
|
|
}
|
|
}
|
|
minbleicresults(&blcstate, &pcr, &blcrep, _state);
|
|
ae_assert(blcrep.terminationtype>0, "FitSphereX: unexpected failure of BLEIC solver", _state);
|
|
rep->iterationscount = rep->iterationscount+blcrep.iterationscount;
|
|
|
|
/*
|
|
* Terminate iterations early if we converged
|
|
*/
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(prevc.ptr.p_double[j]-pcr.ptr.p_double[j], _state);
|
|
}
|
|
v = ae_sqrt(v, _state);
|
|
if( ae_fp_less_eq(v,epsx) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Offload center coordinates from PCR to CX,
|
|
* re-calculate exact value of RLo/RHi using CX.
|
|
*/
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
cx->ptr.p_double[j] = pcr.ptr.p_double[j];
|
|
}
|
|
*rlo = ae_maxrealnumber;
|
|
*rhi = (double)(0);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
v = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = v+ae_sqr(xy->ptr.pp_double[i][j]-cx->ptr.p_double[j], _state);
|
|
}
|
|
v = ae_sqrt(v, _state);
|
|
*rhi = ae_maxreal(*rhi, v, _state);
|
|
*rlo = ae_minreal(*rlo, v, _state);
|
|
}
|
|
if( !userlo )
|
|
{
|
|
*rlo = (double)(0);
|
|
}
|
|
if( !userhi )
|
|
{
|
|
*rhi = (double)(0);
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Oooops...!
|
|
*/
|
|
ae_assert(ae_false, "FitSphereX: integrity check failed", _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
void _fitsphereinternalreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
fitsphereinternalreport *p = (fitsphereinternalreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _fitsphereinternalreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
fitsphereinternalreport *dst = (fitsphereinternalreport*)_dst;
|
|
fitsphereinternalreport *src = (fitsphereinternalreport*)_src;
|
|
dst->nfev = src->nfev;
|
|
dst->iterationscount = src->iterationscount;
|
|
}
|
|
|
|
|
|
void _fitsphereinternalreport_clear(void* _p)
|
|
{
|
|
fitsphereinternalreport *p = (fitsphereinternalreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _fitsphereinternalreport_destroy(void* _p)
|
|
{
|
|
fitsphereinternalreport *p = (fitsphereinternalreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_INTFITSERV) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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);
|
|
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,(double)(0)) )
|
|
{
|
|
xmin = (double)(-1);
|
|
xmax = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater(xmin,(double)(0)) )
|
|
{
|
|
xmin = 0.5*xmin;
|
|
}
|
|
else
|
|
{
|
|
xmax = 0.5*xmax;
|
|
}
|
|
}
|
|
}
|
|
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)), (double)(dc->ptr.p_int[i]), _state);
|
|
}
|
|
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 = (double)(0);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
*sa = *sa+y->ptr.p_double[i];
|
|
}
|
|
*sa = *sa/n;
|
|
*sb = (double)(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));
|
|
}
|
|
}
|
|
mx = (double)(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,(double)(0)) )
|
|
{
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
w->ptr.p_double[i] = w->ptr.p_double[i]/mx;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void buildpriorterm(/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t modeltype,
|
|
double priorval,
|
|
/* Real */ ae_matrix* v,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
double rj;
|
|
ae_matrix araw;
|
|
ae_matrix amod;
|
|
ae_matrix braw;
|
|
ae_vector tmp0;
|
|
double lambdareg;
|
|
ae_int_t rfsits;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&araw, 0, sizeof(araw));
|
|
memset(&amod, 0, sizeof(amod));
|
|
memset(&braw, 0, sizeof(braw));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
ae_matrix_clear(v);
|
|
ae_matrix_init(&araw, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&amod, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&braw, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=0, "BuildPriorTerm: N<0", _state);
|
|
ae_assert(nx>0, "BuildPriorTerm: NX<=0", _state);
|
|
ae_assert(ny>0, "BuildPriorTerm: NY<=0", _state);
|
|
ae_matrix_set_length(v, ny, nx+1, _state);
|
|
for(i=0; i<=v->rows-1; i++)
|
|
{
|
|
for(j=0; j<=v->cols-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
if( n==0 )
|
|
{
|
|
if( modeltype==0 )
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
v->ptr.pp_double[i][nx] = priorval;
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==1 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==2 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==3 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "BuildPriorTerm: unexpected model type", _state);
|
|
}
|
|
if( modeltype==0 )
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
v->ptr.pp_double[i][nx] = priorval;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
xy->ptr.pp_double[i][nx+j] = xy->ptr.pp_double[i][nx+j]-priorval;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==2 )
|
|
{
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v->ptr.pp_double[j][nx] = v->ptr.pp_double[j][nx]+xy->ptr.pp_double[i][nx+j];
|
|
}
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v->ptr.pp_double[j][nx] = v->ptr.pp_double[j][nx]/coalesce((double)(n), (double)(1), _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
xy->ptr.pp_double[i][nx+j] = xy->ptr.pp_double[i][nx+j]-v->ptr.pp_double[j][nx];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==3 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_assert(modeltype==1, "BuildPriorTerm: unexpected model type", _state);
|
|
lambdareg = 0.0;
|
|
ae_matrix_set_length(&araw, nx+1, nx+1, _state);
|
|
ae_matrix_set_length(&braw, nx+1, ny, _state);
|
|
ae_vector_set_length(&tmp0, nx+1, _state);
|
|
ae_matrix_set_length(&amod, nx+1, nx+1, _state);
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
araw.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
tmp0.ptr.p_double[j] = xy->ptr.pp_double[i][j];
|
|
}
|
|
tmp0.ptr.p_double[nx] = 1.0;
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
for(j1=0; j1<=nx; j1++)
|
|
{
|
|
araw.ptr.pp_double[j0][j1] = araw.ptr.pp_double[j0][j1]+tmp0.ptr.p_double[j0]*tmp0.ptr.p_double[j1];
|
|
}
|
|
}
|
|
}
|
|
for(rfsits=1; rfsits<=3; rfsits++)
|
|
{
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
braw.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
tmp0.ptr.p_double[j] = xy->ptr.pp_double[i][j];
|
|
}
|
|
tmp0.ptr.p_double[nx] = 1.0;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rj = xy->ptr.pp_double[i][nx+j];
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
rj = rj-tmp0.ptr.p_double[j0]*v->ptr.pp_double[j][j0];
|
|
}
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
braw.ptr.pp_double[j0][j] = braw.ptr.pp_double[j0][j]+rj*tmp0.ptr.p_double[j0];
|
|
}
|
|
}
|
|
}
|
|
for(;;)
|
|
{
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
amod.ptr.pp_double[i][j] = araw.ptr.pp_double[i][j];
|
|
}
|
|
amod.ptr.pp_double[i][i] = amod.ptr.pp_double[i][i]+lambdareg*coalesce(amod.ptr.pp_double[i][i], (double)(1), _state);
|
|
}
|
|
if( spdmatrixcholesky(&amod, nx+1, ae_true, _state) )
|
|
{
|
|
break;
|
|
}
|
|
lambdareg = coalesce(10*lambdareg, 1.0E-12, _state);
|
|
}
|
|
rmatrixlefttrsm(nx+1, ny, &amod, 0, 0, ae_true, ae_false, 1, &braw, 0, 0, _state);
|
|
rmatrixlefttrsm(nx+1, ny, &amod, 0, 0, ae_true, ae_false, 0, &braw, 0, 0, _state);
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v->ptr.pp_double[j][i] = v->ptr.pp_double[j][i]+braw.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
tmp0.ptr.p_double[j] = xy->ptr.pp_double[i][j];
|
|
}
|
|
tmp0.ptr.p_double[nx] = 1.0;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rj = 0.0;
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
rj = rj+tmp0.ptr.p_double[j0]*v->ptr.pp_double[j][j0];
|
|
}
|
|
xy->ptr.pp_double[i][nx+j] = xy->ptr.pp_double[i][nx+j]-rj;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
void buildpriorterm1(/* Real */ ae_vector* xy1,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t modeltype,
|
|
double priorval,
|
|
/* Real */ ae_matrix* v,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t ew;
|
|
double rj;
|
|
ae_matrix araw;
|
|
ae_matrix amod;
|
|
ae_matrix braw;
|
|
ae_vector tmp0;
|
|
double lambdareg;
|
|
ae_int_t rfsits;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&araw, 0, sizeof(araw));
|
|
memset(&amod, 0, sizeof(amod));
|
|
memset(&braw, 0, sizeof(braw));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
ae_matrix_clear(v);
|
|
ae_matrix_init(&araw, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&amod, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&braw, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=0, "BuildPriorTerm: N<0", _state);
|
|
ae_assert(nx>0, "BuildPriorTerm: NX<=0", _state);
|
|
ae_assert(ny>0, "BuildPriorTerm: NY<=0", _state);
|
|
ew = nx+ny;
|
|
ae_matrix_set_length(v, ny, nx+1, _state);
|
|
for(i=0; i<=v->rows-1; i++)
|
|
{
|
|
for(j=0; j<=v->cols-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
if( n==0 )
|
|
{
|
|
if( modeltype==0 )
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
v->ptr.pp_double[i][nx] = priorval;
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==1 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==2 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==3 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "BuildPriorTerm: unexpected model type", _state);
|
|
}
|
|
if( modeltype==0 )
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
v->ptr.pp_double[i][nx] = priorval;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
xy1->ptr.p_double[i*ew+nx+j] = xy1->ptr.p_double[i*ew+nx+j]-priorval;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==2 )
|
|
{
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v->ptr.pp_double[j][nx] = v->ptr.pp_double[j][nx]+xy1->ptr.p_double[i*ew+nx+j];
|
|
}
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v->ptr.pp_double[j][nx] = v->ptr.pp_double[j][nx]/coalesce((double)(n), (double)(1), _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
xy1->ptr.p_double[i*ew+nx+j] = xy1->ptr.p_double[i*ew+nx+j]-v->ptr.pp_double[j][nx];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( modeltype==3 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_assert(modeltype==1, "BuildPriorTerm: unexpected model type", _state);
|
|
lambdareg = 0.0;
|
|
ae_matrix_set_length(&araw, nx+1, nx+1, _state);
|
|
ae_matrix_set_length(&braw, nx+1, ny, _state);
|
|
ae_vector_set_length(&tmp0, nx+1, _state);
|
|
ae_matrix_set_length(&amod, nx+1, nx+1, _state);
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
araw.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
tmp0.ptr.p_double[j] = xy1->ptr.p_double[i*ew+j];
|
|
}
|
|
tmp0.ptr.p_double[nx] = 1.0;
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
for(j1=0; j1<=nx; j1++)
|
|
{
|
|
araw.ptr.pp_double[j0][j1] = araw.ptr.pp_double[j0][j1]+tmp0.ptr.p_double[j0]*tmp0.ptr.p_double[j1];
|
|
}
|
|
}
|
|
}
|
|
for(rfsits=1; rfsits<=3; rfsits++)
|
|
{
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
braw.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
tmp0.ptr.p_double[j] = xy1->ptr.p_double[i*ew+j];
|
|
}
|
|
tmp0.ptr.p_double[nx] = 1.0;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rj = xy1->ptr.p_double[i*ew+nx+j];
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
rj = rj-tmp0.ptr.p_double[j0]*v->ptr.pp_double[j][j0];
|
|
}
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
braw.ptr.pp_double[j0][j] = braw.ptr.pp_double[j0][j]+rj*tmp0.ptr.p_double[j0];
|
|
}
|
|
}
|
|
}
|
|
for(;;)
|
|
{
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
amod.ptr.pp_double[i][j] = araw.ptr.pp_double[i][j];
|
|
}
|
|
amod.ptr.pp_double[i][i] = amod.ptr.pp_double[i][i]+lambdareg*coalesce(amod.ptr.pp_double[i][i], (double)(1), _state);
|
|
}
|
|
if( spdmatrixcholesky(&amod, nx+1, ae_true, _state) )
|
|
{
|
|
break;
|
|
}
|
|
lambdareg = coalesce(10*lambdareg, 1.0E-12, _state);
|
|
}
|
|
rmatrixlefttrsm(nx+1, ny, &amod, 0, 0, ae_true, ae_false, 1, &braw, 0, 0, _state);
|
|
rmatrixlefttrsm(nx+1, ny, &amod, 0, 0, ae_true, ae_false, 0, &braw, 0, 0, _state);
|
|
for(i=0; i<=nx; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v->ptr.pp_double[j][i] = v->ptr.pp_double[j][i]+braw.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
tmp0.ptr.p_double[j] = xy1->ptr.p_double[i*ew+j];
|
|
}
|
|
tmp0.ptr.p_double[nx] = 1.0;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rj = 0.0;
|
|
for(j0=0; j0<=nx; j0++)
|
|
{
|
|
rj = rj+tmp0.ptr.p_double[j0]*v->ptr.pp_double[j][j0];
|
|
}
|
|
xy1->ptr.p_double[i*ew+nx+j] = xy1->ptr.p_double[i*ew+nx+j]-rj;
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_SPLINE1D) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
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] = (double)(0);
|
|
c->c.ptr.p_double[4*i+3] = (double)(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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&a1, 0, sizeof(a1));
|
|
memset(&a2, 0, sizeof(a2));
|
|
memset(&a3, 0, sizeof(a3));
|
|
memset(&b, 0, sizeof(b));
|
|
memset(&dt, 0, sizeof(dt));
|
|
memset(&d, 0, sizeof(d));
|
|
memset(&p, 0, sizeof(p));
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&a1, 0, sizeof(a1));
|
|
memset(&a2, 0, sizeof(a2));
|
|
memset(&a3, 0, sizeof(a3));
|
|
memset(&b, 0, sizeof(b));
|
|
memset(&dt, 0, sizeof(dt));
|
|
memset(&p, 0, sizeof(p));
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&a1, 0, sizeof(a1));
|
|
memset(&a2, 0, sizeof(a2));
|
|
memset(&a3, 0, sizeof(a3));
|
|
memset(&b, 0, sizeof(b));
|
|
memset(&dt, 0, sizeof(dt));
|
|
memset(&p, 0, sizeof(p));
|
|
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 = (double)(0);
|
|
s2 = (double)(0);
|
|
s3 = (double)(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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_x2, 0, sizeof(_x2));
|
|
memset(&a1, 0, sizeof(a1));
|
|
memset(&a2, 0, sizeof(a2));
|
|
memset(&a3, 0, sizeof(a3));
|
|
memset(&b, 0, sizeof(b));
|
|
memset(&d, 0, sizeof(d));
|
|
memset(&dt, 0, sizeof(dt));
|
|
memset(&d1, 0, sizeof(d1));
|
|
memset(&d2, 0, sizeof(d2));
|
|
memset(&p, 0, sizeof(p));
|
|
memset(&p2, 0, sizeof(p2));
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_x2, 0, sizeof(_x2));
|
|
memset(&a1, 0, sizeof(a1));
|
|
memset(&a2, 0, sizeof(a2));
|
|
memset(&a3, 0, sizeof(a3));
|
|
memset(&b, 0, sizeof(b));
|
|
memset(&d, 0, sizeof(d));
|
|
memset(&dt, 0, sizeof(dt));
|
|
memset(&rt1, 0, sizeof(rt1));
|
|
memset(&p, 0, sizeof(p));
|
|
memset(&p2, 0, sizeof(p2));
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_x2, 0, sizeof(_x2));
|
|
memset(&a1, 0, sizeof(a1));
|
|
memset(&a2, 0, sizeof(a2));
|
|
memset(&a3, 0, sizeof(a3));
|
|
memset(&b, 0, sizeof(b));
|
|
memset(&d, 0, sizeof(d));
|
|
memset(&dt, 0, sizeof(dt));
|
|
memset(&p, 0, sizeof(p));
|
|
memset(&p2, 0, sizeof(p2));
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&d, 0, sizeof(d));
|
|
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,(double)(0)), "Spline1DBuildCatmullRom: Tension<0!", _state);
|
|
ae_assert(ae_fp_less_eq(tension,(double)(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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_d, 0, sizeof(_d));
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&d, 0, sizeof(d));
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&diff, 0, sizeof(diff));
|
|
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),(double)(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);
|
|
memset(&x, 0, sizeof(x));
|
|
memset(&y, 0, sizeof(y));
|
|
memset(&d, 0, sizeof(d));
|
|
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,(double)(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 = (double)(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 = (double)(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 = (double)(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;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Fitting by smoothing (penalized) cubic spline.
|
|
|
|
This function approximates N scattered points (some of X[] may be equal to
|
|
each other) by cubic spline with M nodes at equidistant grid spanning
|
|
interval [min(x,xc),max(x,xc)].
|
|
|
|
The problem is regularized by adding nonlinearity penalty to usual least
|
|
squares penalty function:
|
|
|
|
MERIT_FUNC = F_LS + F_NL
|
|
|
|
where F_LS is a least squares error term, and F_NL is a nonlinearity
|
|
penalty which is roughly proportional to LambdaNS*integral{ S''(x)^2*dx }.
|
|
Algorithm applies automatic renormalization of F_NL which makes penalty
|
|
term roughly invariant to scaling of X[] and changes in M.
|
|
|
|
This function is a new edition of penalized regression spline fitting,
|
|
a fast and compact one which needs much less resources that its previous
|
|
version: just O(maxMN) memory and O(maxMN*log(maxMN)) time.
|
|
|
|
NOTE: it is OK to run this function with both M<<N and M>>N; say, it is
|
|
possible to process 100 points with 1000-node spline.
|
|
|
|
INPUT PARAMETERS:
|
|
X - points, array[0..N-1].
|
|
Y - function values, array[0..N-1].
|
|
N - number of points (optional):
|
|
* N>0
|
|
* if given, only first N elements of X/Y are processed
|
|
* if not given, automatically determined from lengths
|
|
M - number of basis functions ( = number_of_nodes), M>=4.
|
|
LambdaNS - LambdaNS>=0, regularization constant passed by user.
|
|
It penalizes nonlinearity in the regression spline.
|
|
Possible values to start from are 0.00001, 0.1, 1
|
|
|
|
OUTPUT PARAMETERS:
|
|
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
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 27.08.2019 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline1dfit(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
double lambdans,
|
|
spline1dinterpolant* s,
|
|
spline1dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_int_t bfrad;
|
|
double xa;
|
|
double xb;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
double v;
|
|
double dv;
|
|
double d2v;
|
|
ae_int_t gridexpansion;
|
|
ae_vector xywork;
|
|
ae_matrix vterm;
|
|
ae_vector sx;
|
|
ae_vector sy;
|
|
ae_vector sdy;
|
|
ae_vector tmpx;
|
|
ae_vector tmpy;
|
|
spline1dinterpolant basis1;
|
|
sparsematrix av;
|
|
sparsematrix ah;
|
|
sparsematrix ata;
|
|
ae_vector targets;
|
|
double meany;
|
|
ae_int_t lsqrcnt;
|
|
ae_int_t nrel;
|
|
double rss;
|
|
double tss;
|
|
ae_int_t arows;
|
|
ae_vector tmp0;
|
|
ae_vector tmp1;
|
|
linlsqrstate solver;
|
|
linlsqrreport srep;
|
|
double creg;
|
|
double mxata;
|
|
ae_int_t bw;
|
|
ae_vector nzidx;
|
|
ae_vector nzval;
|
|
ae_int_t nzcnt;
|
|
double scaletargetsby;
|
|
double scalepenaltyby;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&xywork, 0, sizeof(xywork));
|
|
memset(&vterm, 0, sizeof(vterm));
|
|
memset(&sx, 0, sizeof(sx));
|
|
memset(&sy, 0, sizeof(sy));
|
|
memset(&sdy, 0, sizeof(sdy));
|
|
memset(&tmpx, 0, sizeof(tmpx));
|
|
memset(&tmpy, 0, sizeof(tmpy));
|
|
memset(&basis1, 0, sizeof(basis1));
|
|
memset(&av, 0, sizeof(av));
|
|
memset(&ah, 0, sizeof(ah));
|
|
memset(&ata, 0, sizeof(ata));
|
|
memset(&targets, 0, sizeof(targets));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
memset(&tmp1, 0, sizeof(tmp1));
|
|
memset(&solver, 0, sizeof(solver));
|
|
memset(&srep, 0, sizeof(srep));
|
|
memset(&nzidx, 0, sizeof(nzidx));
|
|
memset(&nzval, 0, sizeof(nzval));
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
_spline1dinterpolant_clear(s);
|
|
_spline1dfitreport_clear(rep);
|
|
ae_vector_init(&xywork, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&vterm, 0, 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(&sdy, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpy, 0, DT_REAL, _state, ae_true);
|
|
_spline1dinterpolant_init(&basis1, _state, ae_true);
|
|
_sparsematrix_init(&av, _state, ae_true);
|
|
_sparsematrix_init(&ah, _state, ae_true);
|
|
_sparsematrix_init(&ata, _state, ae_true);
|
|
ae_vector_init(&targets, 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);
|
|
_linlsqrstate_init(&solver, _state, ae_true);
|
|
_linlsqrreport_init(&srep, _state, ae_true);
|
|
ae_vector_init(&nzidx, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&nzval, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=1, "Spline1DFit: N<1!", _state);
|
|
ae_assert(m>=1, "Spline1DFit: M<1!", _state);
|
|
ae_assert(x->cnt>=n, "Spline1DFit: Length(X)<N!", _state);
|
|
ae_assert(y->cnt>=n, "Spline1DFit: Length(Y)<N!", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "Spline1DFit: X contains infinite or NAN values!", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "Spline1DFit: Y contains infinite or NAN values!", _state);
|
|
ae_assert(ae_isfinite(lambdans, _state), "Spline1DFit: LambdaNS is infinite!", _state);
|
|
ae_assert(ae_fp_greater_eq(lambdans,(double)(0)), "Spline1DFit: LambdaNS<0!", _state);
|
|
bfrad = 2;
|
|
lsqrcnt = 10;
|
|
|
|
/*
|
|
* Sort points.
|
|
* Determine actual area size, make sure that XA<XB
|
|
*/
|
|
tagsortfastr(x, y, &tmpx, &tmpy, n, _state);
|
|
xa = x->ptr.p_double[0];
|
|
xb = x->ptr.p_double[n-1];
|
|
if( ae_fp_eq(xa,xb) )
|
|
{
|
|
v = xa;
|
|
if( ae_fp_greater_eq(v,(double)(0)) )
|
|
{
|
|
xa = v/2-1;
|
|
xb = v*2+1;
|
|
}
|
|
else
|
|
{
|
|
xa = v*2-1;
|
|
xb = v/2+1;
|
|
}
|
|
}
|
|
ae_assert(ae_fp_less(xa,xb), "Spline1DFit: integrity error", _state);
|
|
|
|
/*
|
|
* Perform a grid correction according to current grid expansion size.
|
|
*/
|
|
m = ae_maxint(m, 4, _state);
|
|
gridexpansion = 1;
|
|
v = (xb-xa)/m;
|
|
xa = xa-v*gridexpansion;
|
|
xb = xb+v*gridexpansion;
|
|
m = m+2*gridexpansion;
|
|
|
|
/*
|
|
* Convert X/Y to work representation, remove linear trend (in
|
|
* order to improve condition number).
|
|
*
|
|
* Compute total-sum-of-squares (needed later for R2 coefficient).
|
|
*/
|
|
ae_vector_set_length(&xywork, 2*n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
xywork.ptr.p_double[2*i+0] = (x->ptr.p_double[i]-xa)/(xb-xa);
|
|
xywork.ptr.p_double[2*i+1] = y->ptr.p_double[i];
|
|
}
|
|
buildpriorterm1(&xywork, n, 1, 1, 1, 0.0, &vterm, _state);
|
|
meany = (double)(0);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
meany = meany+y->ptr.p_double[i];
|
|
}
|
|
meany = meany/n;
|
|
tss = (double)(0);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
tss = tss+ae_sqr(y->ptr.p_double[i]-meany, _state);
|
|
}
|
|
|
|
/*
|
|
* Build 1D compact basis function
|
|
* Generate design matrix AV ("vertical") and its transpose AH ("horizontal").
|
|
*/
|
|
ae_vector_set_length(&tmpx, 7, _state);
|
|
ae_vector_set_length(&tmpy, 7, _state);
|
|
tmpx.ptr.p_double[0] = -(double)3/(double)(m-1);
|
|
tmpx.ptr.p_double[1] = -(double)2/(double)(m-1);
|
|
tmpx.ptr.p_double[2] = -(double)1/(double)(m-1);
|
|
tmpx.ptr.p_double[3] = (double)0/(double)(m-1);
|
|
tmpx.ptr.p_double[4] = (double)1/(double)(m-1);
|
|
tmpx.ptr.p_double[5] = (double)2/(double)(m-1);
|
|
tmpx.ptr.p_double[6] = (double)3/(double)(m-1);
|
|
tmpy.ptr.p_double[0] = (double)(0);
|
|
tmpy.ptr.p_double[1] = (double)(0);
|
|
tmpy.ptr.p_double[2] = (double)1/(double)12;
|
|
tmpy.ptr.p_double[3] = (double)2/(double)6;
|
|
tmpy.ptr.p_double[4] = (double)1/(double)12;
|
|
tmpy.ptr.p_double[5] = (double)(0);
|
|
tmpy.ptr.p_double[6] = (double)(0);
|
|
spline1dbuildcubic(&tmpx, &tmpy, tmpx.cnt, 2, 0.0, 2, 0.0, &basis1, _state);
|
|
arows = n+2*m;
|
|
sparsecreate(arows, m, 0, &av, _state);
|
|
setlengthzero(&targets, arows, _state);
|
|
scaletargetsby = 1/ae_sqrt((double)(n), _state);
|
|
scalepenaltyby = 1/ae_sqrt((double)(m), _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Generate design matrix row #I which corresponds to I-th dataset point
|
|
*/
|
|
k = ae_ifloor(boundval(xywork.ptr.p_double[2*i+0]*(m-1), (double)(0), (double)(m-1), _state), _state);
|
|
k0 = ae_maxint(k-(bfrad-1), 0, _state);
|
|
k1 = ae_minint(k+bfrad, m-1, _state);
|
|
for(j=k0; j<=k1; j++)
|
|
{
|
|
sparseset(&av, i, j, spline1dcalc(&basis1, xywork.ptr.p_double[2*i+0]-(double)j/(double)(m-1), _state)*scaletargetsby, _state);
|
|
}
|
|
targets.ptr.p_double[i] = xywork.ptr.p_double[2*i+1]*scaletargetsby;
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Generate design matrix row #(I+N) which corresponds to nonlinearity penalty at I-th node
|
|
*/
|
|
k0 = ae_maxint(i-(bfrad-1), 0, _state);
|
|
k1 = ae_minint(i+(bfrad-1), m-1, _state);
|
|
for(j=k0; j<=k1; j++)
|
|
{
|
|
spline1ddiff(&basis1, (double)i/(double)(m-1)-(double)j/(double)(m-1), &v, &dv, &d2v, _state);
|
|
sparseset(&av, n+i, j, lambdans*d2v*scalepenaltyby, _state);
|
|
}
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Generate design matrix row #(I+N+M) which corresponds to regularization for I-th coefficient
|
|
*/
|
|
sparseset(&av, n+m+i, i, spline1d_lambdareg, _state);
|
|
}
|
|
sparseconverttocrs(&av, _state);
|
|
sparsecopytransposecrs(&av, &ah, _state);
|
|
|
|
/*
|
|
* Build 7-diagonal (bandwidth=3) normal equations matrix and perform Cholesky
|
|
* decomposition (to be used later as preconditioner for LSQR iterations).
|
|
*/
|
|
bw = 3;
|
|
sparsecreatesksband(m, m, bw, &ata, _state);
|
|
mxata = (double)(0);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
for(j=i; j<=ae_minint(i+bw, m-1, _state); j++)
|
|
{
|
|
|
|
/*
|
|
* Get pattern of nonzeros in one of the rows (let it be I-th one)
|
|
* and compute dot product only for nonzero entries.
|
|
*/
|
|
sparsegetcompressedrow(&ah, i, &nzidx, &nzval, &nzcnt, _state);
|
|
v = (double)(0);
|
|
for(k=0; k<=nzcnt-1; k++)
|
|
{
|
|
v = v+sparseget(&ah, i, nzidx.ptr.p_int[k], _state)*sparseget(&ah, j, nzidx.ptr.p_int[k], _state);
|
|
}
|
|
|
|
/*
|
|
* Update ATA and max(ATA)
|
|
*/
|
|
sparseset(&ata, i, j, v, _state);
|
|
if( i==j )
|
|
{
|
|
mxata = ae_maxreal(mxata, ae_fabs(v, _state), _state);
|
|
}
|
|
}
|
|
}
|
|
mxata = coalesce(mxata, 1.0, _state);
|
|
creg = spline1d_cholreg;
|
|
for(;;)
|
|
{
|
|
|
|
/*
|
|
* Regularization
|
|
*/
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sparseset(&ata, i, i, sparseget(&ata, i, i, _state)+mxata*creg, _state);
|
|
}
|
|
|
|
/*
|
|
* Try Cholesky factorization.
|
|
*/
|
|
if( !sparsecholeskyskyline(&ata, m, ae_true, _state) )
|
|
{
|
|
|
|
/*
|
|
* Factorization failed, increase regularizer and repeat
|
|
*/
|
|
creg = coalesce(10*creg, 1.0E-12, _state);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Solve with preconditioned LSQR:
|
|
*
|
|
* use Cholesky factor U of squared design matrix A'*A to
|
|
* transform min|A*x-b| to min|[A*inv(U)]*y-b| with y=U*x.
|
|
*
|
|
* Preconditioned problem is solved with LSQR solver, which
|
|
* gives superior results to normal equations approach. Due
|
|
* to Cholesky preconditioner being utilized we can solve
|
|
* problem in just a few iterations.
|
|
*/
|
|
rvectorsetlengthatleast(&tmp0, arows, _state);
|
|
rvectorsetlengthatleast(&tmp1, m, _state);
|
|
linlsqrcreatebuf(arows, m, &solver, _state);
|
|
linlsqrsetb(&solver, &targets, _state);
|
|
linlsqrsetcond(&solver, 1.0E-14, 1.0E-14, lsqrcnt, _state);
|
|
while(linlsqriteration(&solver, _state))
|
|
{
|
|
if( solver.needmv )
|
|
{
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
tmp1.ptr.p_double[i] = solver.x.ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Use Cholesky factorization of the system matrix
|
|
* as preconditioner: solve TRSV(U,Solver.X)
|
|
*/
|
|
sparsetrsv(&ata, ae_true, ae_false, 0, &tmp1, _state);
|
|
|
|
/*
|
|
* After preconditioning is done, multiply by A
|
|
*/
|
|
sparsemv(&av, &tmp1, &solver.mv, _state);
|
|
}
|
|
if( solver.needmtv )
|
|
{
|
|
|
|
/*
|
|
* Multiply by design matrix A
|
|
*/
|
|
sparsemtv(&av, &solver.x, &solver.mtv, _state);
|
|
|
|
/*
|
|
* Multiply by preconditioner: solve TRSV(U',A*Solver.X)
|
|
*/
|
|
sparsetrsv(&ata, ae_true, ae_false, 1, &solver.mtv, _state);
|
|
}
|
|
}
|
|
linlsqrresults(&solver, &tmp1, &srep, _state);
|
|
sparsetrsv(&ata, ae_true, ae_false, 0, &tmp1, _state);
|
|
|
|
/*
|
|
* Generate output spline as a table of spline valued and first
|
|
* derivatives at nodes (used to build Hermite spline)
|
|
*/
|
|
ae_vector_set_length(&sx, m, _state);
|
|
ae_vector_set_length(&sy, m, _state);
|
|
ae_vector_set_length(&sdy, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sx.ptr.p_double[i] = (double)i/(double)(m-1);
|
|
sy.ptr.p_double[i] = (double)(0);
|
|
sdy.ptr.p_double[i] = (double)(0);
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
k0 = ae_maxint(i-(bfrad-1), 0, _state);
|
|
k1 = ae_minint(i+bfrad, m-1, _state);
|
|
for(j=k0; j<=k1; j++)
|
|
{
|
|
spline1ddiff(&basis1, (double)j/(double)(m-1)-(double)i/(double)(m-1), &v, &dv, &d2v, _state);
|
|
sy.ptr.p_double[j] = sy.ptr.p_double[j]+tmp1.ptr.p_double[i]*v;
|
|
sdy.ptr.p_double[j] = sdy.ptr.p_double[j]+tmp1.ptr.p_double[i]*dv;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate model values
|
|
*/
|
|
sparsemv(&av, &tmp1, &tmp0, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
tmp0.ptr.p_double[i] = tmp0.ptr.p_double[i]/scaletargetsby;
|
|
}
|
|
rss = 0.0;
|
|
nrel = 0;
|
|
rep->rmserror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->avgrelerror = (double)(0);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = xywork.ptr.p_double[2*i+1]-tmp0.ptr.p_double[i];
|
|
rss = rss+v*v;
|
|
rep->rmserror = rep->rmserror+ae_sqr(v, _state);
|
|
rep->avgerror = rep->avgerror+ae_fabs(v, _state);
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v, _state), _state);
|
|
if( ae_fp_neq(y->ptr.p_double[i],(double)(0)) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror+ae_fabs(v/y->ptr.p_double[i], _state);
|
|
nrel = nrel+1;
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/n, _state);
|
|
rep->avgerror = rep->avgerror/n;
|
|
rep->avgrelerror = rep->avgrelerror/coalesce((double)(nrel), 1.0, _state);
|
|
|
|
/*
|
|
* Append prior term.
|
|
* Transform spline to original coordinates.
|
|
* Output.
|
|
*/
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sy.ptr.p_double[i] = sy.ptr.p_double[i]+vterm.ptr.pp_double[0][0]*sx.ptr.p_double[i]+vterm.ptr.pp_double[0][1];
|
|
sdy.ptr.p_double[i] = sdy.ptr.p_double[i]+vterm.ptr.pp_double[0][0];
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sx.ptr.p_double[i] = sx.ptr.p_double[i]*(xb-xa)+xa;
|
|
sdy.ptr.p_double[i] = sdy.ptr.p_double[i]/(xb-xa);
|
|
}
|
|
spline1dbuildhermite(&sx, &sy, &sdy, m, s, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 = (double)(0);
|
|
c1 = (double)(0);
|
|
c2 = (double)(0);
|
|
c3 = (double)(0);
|
|
a = (double)(0);
|
|
b = (double)(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);
|
|
memset(&tr, 0, sizeof(tr));
|
|
memset(&tmpr, 0, sizeof(tmpr));
|
|
memset(&tmpe, 0, sizeof(tmpe));
|
|
memset(&tmpet, 0, sizeof(tmpet));
|
|
memset(&tmpc, 0, sizeof(tmpc));
|
|
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);
|
|
memset(&rbuf, 0, sizeof(rbuf));
|
|
memset(&ibuf, 0, sizeof(ibuf));
|
|
memset(&rbuf2, 0, sizeof(rbuf2));
|
|
memset(&ibuf2, 0, sizeof(ibuf2));
|
|
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,(double)(0)) )
|
|
{
|
|
|
|
/*
|
|
*B<>0 and root inside [0;1]
|
|
*one root
|
|
*/
|
|
if( (ae_fp_neq(b,(double)(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,(double)(0)) )
|
|
{
|
|
return;
|
|
}
|
|
*x0 = (-b-ae_sqrt(dd, _state))/(2*a);
|
|
*x1 = (-b+ae_sqrt(dd, _state))/(2*a);
|
|
if( (ae_fp_greater_eq(extr,(double)(1))&&ae_fp_less_eq(*x1,extr))||(ae_fp_less_eq(extr,(double)(0))&&ae_fp_greater_eq(*x1,extr)) )
|
|
{
|
|
*x0 = *x1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*consider case, when the one is 0
|
|
*/
|
|
if( ae_fp_eq(m0,(double)(0)) )
|
|
{
|
|
*x0 = (double)(0);
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
if( ae_fp_eq(m1,(double)(0)) )
|
|
{
|
|
*x0 = (double)(1);
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
*consider case, when both of derivatives is 0
|
|
*/
|
|
if( ae_fp_eq(m0,(double)(0))&&ae_fp_eq(m1,(double)(0)) )
|
|
{
|
|
*x0 = (double)(0);
|
|
*x1 = (double)(1);
|
|
*nr = 2;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
*consider case, when derivative at 0 is 0, and derivative at 1 is't 0
|
|
*/
|
|
if( ae_fp_eq(m0,(double)(0))&&ae_fp_neq(m1,(double)(0)) )
|
|
{
|
|
dd = b*b-4*a*c;
|
|
if( ae_fp_less(dd,(double)(0)) )
|
|
{
|
|
*x0 = (double)(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 = (double)(0);
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater(extr,*x0) )
|
|
{
|
|
*x0 = (double)(0);
|
|
}
|
|
else
|
|
{
|
|
*x1 = (double)(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,(double)(0))&&ae_fp_neq(m0,(double)(0)) )
|
|
{
|
|
dd = b*b-4*a*c;
|
|
if( ae_fp_less(dd,(double)(0)) )
|
|
{
|
|
*x0 = (double)(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 = (double)(1);
|
|
*nr = 1;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_less(extr,*x0) )
|
|
{
|
|
*x0 = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
*x1 = (double)(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,(double)(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,(double)(0))&&ae_fp_eq(mb,(double)(0)))&&ae_fp_eq(pa,pb))&&ae_fp_eq(pa,(double)(0)) )
|
|
{
|
|
*nr = -1;
|
|
*ne = -1;
|
|
return;
|
|
}
|
|
if( (ae_fp_eq(ma,(double)(0))&&ae_fp_eq(mb,(double)(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((double)(0), (double)(1), a, b, *ex0, _state);
|
|
*ex1 = spline1d_rescaleval((double)(0), (double)(1), a, b, *ex1, _state);
|
|
|
|
/*
|
|
*case 3.1
|
|
*no extremums at [A;B]
|
|
*/
|
|
if( *ne==0 )
|
|
{
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, (double)(0), (double)(1), x0, _state);
|
|
if( *nr==1 )
|
|
{
|
|
*x0 = spline1d_rescaleval((double)(0), (double)(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, (double)(0), (double)(1), x0, _state);
|
|
if( *nr==1 )
|
|
{
|
|
*x0 = spline1d_rescaleval((double)(0), (double)(1), a, b, *x0, _state);
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
*nr = 0;
|
|
i = 0;
|
|
tex0 = spline1d_rescaleval(a, b, (double)(0), (double)(1), *ex0, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, (double)(0), tex0, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval((double)(0), tex0, a, *ex0, *x0, _state);
|
|
i = i+1;
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex0, (double)(1), x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex0, (double)(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, (double)(0), (double)(1), x0, _state);
|
|
if( *nr==1 )
|
|
{
|
|
*x0 = spline1d_rescaleval((double)(0), (double)(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, (double)(0), (double)(1), *ex1, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, (double)(0), tex1, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval((double)(0), tex1, a, *ex1, *x0, _state);
|
|
i = i+1;
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex1, (double)(1), x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex1, (double)(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, (double)(0), (double)(1), *ex0, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, (double)(0), tex0, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval((double)(0), tex0, a, *ex0, *x0, _state);
|
|
i = i+1;
|
|
}
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, tex0, (double)(1), x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex0, (double)(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, (double)(0), (double)(1), *ex0, _state);
|
|
tex1 = spline1d_rescaleval(a, b, (double)(0), (double)(1), *ex1, _state);
|
|
*nr = bisectmethod(pa, tmpma, pb, tmpmb, (double)(0), tex0, x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
tempdata->ptr.p_double[i] = spline1d_rescaleval((double)(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, (double)(1), x0, _state)+(*nr);
|
|
if( *nr>i )
|
|
{
|
|
*x0 = spline1d_rescaleval(tex1, (double)(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,(double)(0)) )
|
|
{
|
|
*x = a;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_eq(rf,(double)(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,(double)(0)) )
|
|
{
|
|
*x = a0;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
if( ae_fp_eq(rf,(double)(0)) )
|
|
{
|
|
*x = b0;
|
|
result = 1;
|
|
return result;
|
|
}
|
|
if( ae_fp_eq(mf,(double)(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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&d, 0, sizeof(d));
|
|
memset(&ex, 0, sizeof(ex));
|
|
memset(&ey, 0, sizeof(ey));
|
|
memset(&p, 0, sizeof(p));
|
|
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 = (double)(0);
|
|
do
|
|
{
|
|
ca = ey.ptr.p_double[i+1]-ey.ptr.p_double[i];
|
|
i = i+1;
|
|
}
|
|
while(!(ae_fp_neq(ca,(double)(0))||i>n-2));
|
|
if( ae_fp_neq(ca,(double)(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,(double)(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] = (double)(0);
|
|
d.ptr.p_double[sn-1] = (double)(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] = (double)(0);
|
|
d.ptr.p_double[j+1] = (double)(0);
|
|
}
|
|
else
|
|
{
|
|
alpha = d.ptr.p_double[j]/delta;
|
|
beta = d.ptr.p_double[j+1]/delta;
|
|
if( ae_fp_neq(alpha,(double)(0)) )
|
|
{
|
|
cb = alpha*ae_sqrt(1+ae_sqr(beta/alpha, _state), _state);
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_neq(beta,(double)(0)) )
|
|
{
|
|
cb = beta;
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
if( ae_fp_greater(cb,(double)(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] = (double)(0);
|
|
d->ptr.p_double[1] = (double)(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] = (double)(0);
|
|
a2->ptr.p_double[0] = (double)(1);
|
|
a3->ptr.p_double[0] = (double)(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] = (double)(0);
|
|
a2->ptr.p_double[0] = (double)(1);
|
|
a3->ptr.p_double[0] = (double)(0);
|
|
b->ptr.p_double[0] = boundl;
|
|
}
|
|
if( boundltype==2 )
|
|
{
|
|
a1->ptr.p_double[0] = (double)(0);
|
|
a2->ptr.p_double[0] = (double)(2);
|
|
a3->ptr.p_double[0] = (double)(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] = (double)(1);
|
|
a2->ptr.p_double[n-1] = (double)(1);
|
|
a3->ptr.p_double[n-1] = (double)(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] = (double)(0);
|
|
a2->ptr.p_double[n-1] = (double)(1);
|
|
a3->ptr.p_double[n-1] = (double)(0);
|
|
b->ptr.p_double[n-1] = boundr;
|
|
}
|
|
if( boundrtype==2 )
|
|
{
|
|
a1->ptr.p_double[n-1] = (double)(1);
|
|
a2->ptr.p_double[n-1] = (double)(2);
|
|
a3->ptr.p_double[n-1] = (double)(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);
|
|
memset(&bufx, 0, sizeof(bufx));
|
|
memset(&bufy, 0, sizeof(bufy));
|
|
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);
|
|
memset(&rbuf, 0, sizeof(rbuf));
|
|
memset(&ibuf, 0, sizeof(ibuf));
|
|
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);
|
|
memset(&_b, 0, sizeof(_b));
|
|
memset(&_d, 0, sizeof(_d));
|
|
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);
|
|
memset(&_b, 0, sizeof(_b));
|
|
memset(&y, 0, sizeof(y));
|
|
memset(&z, 0, sizeof(z));
|
|
memset(&u, 0, sizeof(u));
|
|
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] = (double)(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;
|
|
}
|
|
|
|
|
|
void _spline1dinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline1dinterpolant *p = (spline1dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _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;
|
|
ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void _spline1dfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline1dfitreport *p = (spline1dfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _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;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_PARAMETRIC) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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);
|
|
memset(&_xy, 0, sizeof(_xy));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
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
|
|
*/
|
|
parametric_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);
|
|
memset(&_xy, 0, sizeof(_xy));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
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
|
|
*/
|
|
parametric_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);
|
|
memset(&_xy, 0, sizeof(_xy));
|
|
memset(&xyp, 0, sizeof(xyp));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
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
|
|
*/
|
|
parametric_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);
|
|
memset(&_xy, 0, sizeof(_xy));
|
|
memset(&xyp, 0, sizeof(xyp));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
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
|
|
*/
|
|
parametric_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] = (double)(0);
|
|
if( !p->periodic )
|
|
{
|
|
t->ptr.p_double[*n-1] = (double)(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] = (double)(0);
|
|
if( !p->periodic )
|
|
{
|
|
t->ptr.p_double[*n-1] = (double)(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,(double)(0))||ae_fp_neq(*y,(double)(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,(double)(0))||ae_fp_neq(*y,(double)(0)))||ae_fp_neq(*z,(double)(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);
|
|
memset(&state, 0, sizeof(state));
|
|
memset(&rep, 0, sizeof(rep));
|
|
_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);
|
|
memset(&state, 0, sizeof(state));
|
|
memset(&rep, 0, sizeof(rep));
|
|
_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;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine fits piecewise linear curve to points with Ramer-Douglas-
|
|
Peucker algorithm. This function performs PARAMETRIC fit, i.e. it can be
|
|
used to fit curves like circles.
|
|
|
|
On input it accepts dataset which describes parametric multidimensional
|
|
curve X(t), with X being vector, and t taking values in [0,N), where N is
|
|
a number of points in dataset. As result, it returns reduced dataset X2,
|
|
which can be used to build parametric curve X2(t), which approximates
|
|
X(t) with desired precision (or has specified number of sections).
|
|
|
|
|
|
INPUT PARAMETERS:
|
|
X - array of multidimensional points:
|
|
* at least N elements, leading N elements are used if more
|
|
than N elements were specified
|
|
* order of points is IMPORTANT because it is parametric
|
|
fit
|
|
* each row of array is one point which has D coordinates
|
|
N - number of elements in X
|
|
D - number of dimensions (elements per row of X)
|
|
StopM - stopping condition - desired number of sections:
|
|
* at most M sections are generated by this function
|
|
* less than M sections can be generated if we have N<M
|
|
(or some X are non-distinct).
|
|
* zero StopM means that algorithm does not stop after
|
|
achieving some pre-specified section count
|
|
StopEps - stopping condition - desired precision:
|
|
* algorithm stops after error in each section is at most Eps
|
|
* zero Eps means that algorithm does not stop after
|
|
achieving some pre-specified precision
|
|
|
|
OUTPUT PARAMETERS:
|
|
X2 - array of corner points for piecewise approximation,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
Idx2 - array of indexes (parameter values):
|
|
* has length NSections+1 or zero (for NSections=0).
|
|
* each element of Idx2 corresponds to same-numbered
|
|
element of X2
|
|
* each element of Idx2 is index of corresponding element
|
|
of X2 at original array X, i.e. I-th row of X2 is
|
|
Idx2[I]-th row of X.
|
|
* elements of Idx2 can be treated as parameter values
|
|
which should be used when building new parametric curve
|
|
* Idx2[0]=0, Idx2[NSections]=N-1
|
|
NSections- number of sections found by algorithm, NSections<=M,
|
|
NSections can be zero for degenerate datasets
|
|
(N<=1 or all X[] are non-distinct).
|
|
|
|
NOTE: algorithm stops after:
|
|
a) dividing curve into StopM sections
|
|
b) achieving required precision StopEps
|
|
c) dividing curve into N-1 sections
|
|
If both StopM and StopEps are non-zero, algorithm is stopped by the
|
|
FIRST criterion which is satisfied. In case both StopM and StopEps
|
|
are zero, algorithm stops because of (c).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void parametricrdpfixed(/* Real */ ae_matrix* x,
|
|
ae_int_t n,
|
|
ae_int_t d,
|
|
ae_int_t stopm,
|
|
double stopeps,
|
|
/* Real */ ae_matrix* x2,
|
|
/* Integer */ ae_vector* idx2,
|
|
ae_int_t* nsections,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_bool allsame;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
ae_int_t k2;
|
|
double e0;
|
|
double e1;
|
|
ae_int_t idx0;
|
|
ae_int_t idx1;
|
|
ae_int_t worstidx;
|
|
double worsterror;
|
|
ae_matrix sections;
|
|
ae_vector heaperrors;
|
|
ae_vector heaptags;
|
|
ae_vector buf0;
|
|
ae_vector buf1;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(§ions, 0, sizeof(sections));
|
|
memset(&heaperrors, 0, sizeof(heaperrors));
|
|
memset(&heaptags, 0, sizeof(heaptags));
|
|
memset(&buf0, 0, sizeof(buf0));
|
|
memset(&buf1, 0, sizeof(buf1));
|
|
ae_matrix_clear(x2);
|
|
ae_vector_clear(idx2);
|
|
*nsections = 0;
|
|
ae_matrix_init(§ions, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&heaperrors, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&heaptags, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&buf0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&buf1, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=0, "LSTFitPiecewiseLinearParametricRDP: N<0", _state);
|
|
ae_assert(d>=1, "LSTFitPiecewiseLinearParametricRDP: D<=0", _state);
|
|
ae_assert(stopm>=0, "LSTFitPiecewiseLinearParametricRDP: StopM<1", _state);
|
|
ae_assert(ae_isfinite(stopeps, _state)&&ae_fp_greater_eq(stopeps,(double)(0)), "LSTFitPiecewiseLinearParametricRDP: StopEps<0 or is infinite", _state);
|
|
ae_assert(x->rows>=n, "LSTFitPiecewiseLinearParametricRDP: Rows(X)<N", _state);
|
|
ae_assert(x->cols>=d, "LSTFitPiecewiseLinearParametricRDP: Cols(X)<D", _state);
|
|
ae_assert(apservisfinitematrix(x, n, d, _state), "LSTFitPiecewiseLinearParametricRDP: X contains infinite/NAN values", _state);
|
|
|
|
/*
|
|
* Handle degenerate cases
|
|
*/
|
|
if( n<=1 )
|
|
{
|
|
*nsections = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
allsame = ae_true;
|
|
for(i=1; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
allsame = allsame&&ae_fp_eq(x->ptr.pp_double[i][j],x->ptr.pp_double[0][j]);
|
|
}
|
|
}
|
|
if( allsame )
|
|
{
|
|
*nsections = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Prepare first section
|
|
*/
|
|
parametric_rdpanalyzesectionpar(x, 0, n-1, d, &worstidx, &worsterror, _state);
|
|
ae_matrix_set_length(§ions, n, 4, _state);
|
|
ae_vector_set_length(&heaperrors, n, _state);
|
|
ae_vector_set_length(&heaptags, n, _state);
|
|
*nsections = 1;
|
|
sections.ptr.pp_double[0][0] = (double)(0);
|
|
sections.ptr.pp_double[0][1] = (double)(n-1);
|
|
sections.ptr.pp_double[0][2] = (double)(worstidx);
|
|
sections.ptr.pp_double[0][3] = worsterror;
|
|
heaperrors.ptr.p_double[0] = worsterror;
|
|
heaptags.ptr.p_int[0] = 0;
|
|
ae_assert(ae_fp_eq(sections.ptr.pp_double[0][1],(double)(n-1)), "RDP algorithm: integrity check failed", _state);
|
|
|
|
/*
|
|
* Main loop.
|
|
* Repeatedly find section with worst error and divide it.
|
|
* Terminate after M-th section, or because of other reasons (see loop internals).
|
|
*/
|
|
for(;;)
|
|
{
|
|
|
|
/*
|
|
* Break loop if one of the stopping conditions was met.
|
|
* Store index of worst section to K.
|
|
*/
|
|
if( ae_fp_eq(heaperrors.ptr.p_double[0],(double)(0)) )
|
|
{
|
|
break;
|
|
}
|
|
if( ae_fp_greater(stopeps,(double)(0))&&ae_fp_less_eq(heaperrors.ptr.p_double[0],stopeps) )
|
|
{
|
|
break;
|
|
}
|
|
if( stopm>0&&*nsections>=stopm )
|
|
{
|
|
break;
|
|
}
|
|
k = heaptags.ptr.p_int[0];
|
|
|
|
/*
|
|
* K-th section is divided in two:
|
|
* * first one spans interval from X[Sections[K,0]] to X[Sections[K,2]]
|
|
* * second one spans interval from X[Sections[K,2]] to X[Sections[K,1]]
|
|
*
|
|
* First section is stored at K-th position, second one is appended to the table.
|
|
* Then we update heap which stores pairs of (error,section_index)
|
|
*/
|
|
k0 = ae_round(sections.ptr.pp_double[k][0], _state);
|
|
k1 = ae_round(sections.ptr.pp_double[k][1], _state);
|
|
k2 = ae_round(sections.ptr.pp_double[k][2], _state);
|
|
parametric_rdpanalyzesectionpar(x, k0, k2, d, &idx0, &e0, _state);
|
|
parametric_rdpanalyzesectionpar(x, k2, k1, d, &idx1, &e1, _state);
|
|
sections.ptr.pp_double[k][0] = (double)(k0);
|
|
sections.ptr.pp_double[k][1] = (double)(k2);
|
|
sections.ptr.pp_double[k][2] = (double)(idx0);
|
|
sections.ptr.pp_double[k][3] = e0;
|
|
tagheapreplacetopi(&heaperrors, &heaptags, *nsections, e0, k, _state);
|
|
sections.ptr.pp_double[*nsections][0] = (double)(k2);
|
|
sections.ptr.pp_double[*nsections][1] = (double)(k1);
|
|
sections.ptr.pp_double[*nsections][2] = (double)(idx1);
|
|
sections.ptr.pp_double[*nsections][3] = e1;
|
|
tagheappushi(&heaperrors, &heaptags, nsections, e1, *nsections, _state);
|
|
}
|
|
|
|
/*
|
|
* Convert from sections to indexes
|
|
*/
|
|
ae_vector_set_length(&buf0, *nsections+1, _state);
|
|
for(i=0; i<=*nsections-1; i++)
|
|
{
|
|
buf0.ptr.p_double[i] = (double)(ae_round(sections.ptr.pp_double[i][0], _state));
|
|
}
|
|
buf0.ptr.p_double[*nsections] = (double)(n-1);
|
|
tagsortfast(&buf0, &buf1, *nsections+1, _state);
|
|
ae_vector_set_length(idx2, *nsections+1, _state);
|
|
for(i=0; i<=*nsections; i++)
|
|
{
|
|
idx2->ptr.p_int[i] = ae_round(buf0.ptr.p_double[i], _state);
|
|
}
|
|
ae_assert(idx2->ptr.p_int[0]==0, "RDP algorithm: integrity check failed", _state);
|
|
ae_assert(idx2->ptr.p_int[*nsections]==n-1, "RDP algorithm: integrity check failed", _state);
|
|
|
|
/*
|
|
* Output sections:
|
|
* * first NSection elements of X2/Y2 are filled by x/y at left boundaries of sections
|
|
* * last element of X2/Y2 is filled by right boundary of rightmost section
|
|
* * X2/Y2 is sorted by ascending of X2
|
|
*/
|
|
ae_matrix_set_length(x2, *nsections+1, d, _state);
|
|
for(i=0; i<=*nsections; i++)
|
|
{
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
x2->ptr.pp_double[i][j] = x->ptr.pp_double[idx2->ptr.p_int[i]][j];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Builds non-periodic parameterization for 2-dimensional spline
|
|
*************************************************************************/
|
|
static void parametric_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] = (double)(i);
|
|
}
|
|
}
|
|
if( pt==1 )
|
|
{
|
|
p->ptr.p_double[0] = (double)(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] = (double)(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 parametric_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] = (double)(i);
|
|
}
|
|
}
|
|
if( pt==1 )
|
|
{
|
|
p->ptr.p_double[0] = (double)(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] = (double)(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);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function analyzes section of curve for processing by RDP algorithm:
|
|
given set of points X,Y with indexes [I0,I1] it returns point with
|
|
worst deviation from linear model (PARAMETRIC version which sees curve
|
|
as X(t) with vector X).
|
|
|
|
Input parameters:
|
|
XY - array
|
|
I0,I1 - interval (boundaries included) to process
|
|
D - number of dimensions
|
|
|
|
OUTPUT PARAMETERS:
|
|
WorstIdx - index of worst point
|
|
WorstError - error at worst point
|
|
|
|
NOTE: this function guarantees that it returns exactly zero for a section
|
|
with less than 3 points.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void parametric_rdpanalyzesectionpar(/* Real */ ae_matrix* xy,
|
|
ae_int_t i0,
|
|
ae_int_t i1,
|
|
ae_int_t d,
|
|
ae_int_t* worstidx,
|
|
double* worsterror,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double v;
|
|
double d2;
|
|
double ts;
|
|
double vv;
|
|
|
|
*worstidx = 0;
|
|
*worsterror = 0;
|
|
|
|
|
|
/*
|
|
* Quick exit for 0, 1, 2 points
|
|
*/
|
|
if( i1-i0+1<3 )
|
|
{
|
|
*worstidx = i0;
|
|
*worsterror = 0.0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Estimate D2 - squared distance between XY[I1] and XY[I0].
|
|
* In case D2=0 handle it as special case.
|
|
*/
|
|
d2 = 0.0;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
d2 = d2+ae_sqr(xy->ptr.pp_double[i1][j]-xy->ptr.pp_double[i0][j], _state);
|
|
}
|
|
if( ae_fp_eq(d2,(double)(0)) )
|
|
{
|
|
|
|
/*
|
|
* First and last points are equal, interval evaluation is
|
|
* trivial - we just calculate distance from all points to
|
|
* the first/last one.
|
|
*/
|
|
*worstidx = i0;
|
|
*worsterror = 0.0;
|
|
for(i=i0+1; i<=i1-1; i++)
|
|
{
|
|
vv = 0.0;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
v = xy->ptr.pp_double[i][j]-xy->ptr.pp_double[i0][j];
|
|
vv = vv+v*v;
|
|
}
|
|
vv = ae_sqrt(vv, _state);
|
|
if( ae_fp_greater(vv,*worsterror) )
|
|
{
|
|
*worsterror = vv;
|
|
*worstidx = i;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* General case
|
|
*
|
|
* Current section of curve is modeled as x(t) = d*t+c, where
|
|
* d = XY[I1]-XY[I0]
|
|
* c = XY[I0]
|
|
* t is in [0,1]
|
|
*/
|
|
*worstidx = i0;
|
|
*worsterror = 0.0;
|
|
for(i=i0+1; i<=i1-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Determine t_s - parameter value for projected point.
|
|
*/
|
|
ts = (double)(i-i0)/(double)(i1-i0);
|
|
|
|
/*
|
|
* Estimate error norm
|
|
*/
|
|
vv = 0.0;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
v = (xy->ptr.pp_double[i1][j]-xy->ptr.pp_double[i0][j])*ts-(xy->ptr.pp_double[i][j]-xy->ptr.pp_double[i0][j]);
|
|
vv = vv+ae_sqr(v, _state);
|
|
}
|
|
vv = ae_sqrt(vv, _state);
|
|
if( ae_fp_greater(vv,*worsterror) )
|
|
{
|
|
*worsterror = vv;
|
|
*worstidx = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void _pspline2interpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
pspline2interpolant *p = (pspline2interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->p, 0, DT_REAL, _state, make_automatic);
|
|
_spline1dinterpolant_init(&p->x, _state, make_automatic);
|
|
_spline1dinterpolant_init(&p->y, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _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;
|
|
ae_vector_init_copy(&dst->p, &src->p, _state, make_automatic);
|
|
_spline1dinterpolant_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
_spline1dinterpolant_init_copy(&dst->y, &src->y, _state, make_automatic);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void _pspline3interpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
pspline3interpolant *p = (pspline3interpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->p, 0, DT_REAL, _state, make_automatic);
|
|
_spline1dinterpolant_init(&p->x, _state, make_automatic);
|
|
_spline1dinterpolant_init(&p->y, _state, make_automatic);
|
|
_spline1dinterpolant_init(&p->z, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _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;
|
|
ae_vector_init_copy(&dst->p, &src->p, _state, make_automatic);
|
|
_spline1dinterpolant_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
_spline1dinterpolant_init_copy(&dst->y, &src->y, _state, make_automatic);
|
|
_spline1dinterpolant_init_copy(&dst->z, &src->z, _state, make_automatic);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_SPLINE3D) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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 = (double)(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);
|
|
memset(&x, 0, sizeof(x));
|
|
memset(&y, 0, sizeof(y));
|
|
memset(&z, 0, sizeof(z));
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&v, 0, sizeof(v));
|
|
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,(double)(0))&&ae_fp_neq(ay,(double)(0)))&&ae_fp_neq(az,(double)(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,(double)(0))&&ae_fp_neq(ay,(double)(0)))&&ae_fp_neq(az,(double)(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 = (double)(1);
|
|
bx = (double)(0);
|
|
}
|
|
if( (ae_fp_neq(ax,(double)(0))&&ae_fp_eq(ay,(double)(0)))&&ae_fp_neq(az,(double)(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 = (double)(1);
|
|
by = (double)(0);
|
|
}
|
|
if( (ae_fp_neq(ax,(double)(0))&&ae_fp_neq(ay,(double)(0)))&&ae_fp_eq(az,(double)(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 = (double)(1);
|
|
bz = (double)(0);
|
|
}
|
|
if( (ae_fp_eq(ax,(double)(0))&&ae_fp_eq(ay,(double)(0)))&&ae_fp_neq(az,(double)(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 = (double)(1);
|
|
bx = (double)(0);
|
|
ay = (double)(1);
|
|
by = (double)(0);
|
|
}
|
|
if( (ae_fp_eq(ax,(double)(0))&&ae_fp_neq(ay,(double)(0)))&&ae_fp_eq(az,(double)(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 = (double)(1);
|
|
bx = (double)(0);
|
|
az = (double)(1);
|
|
bz = (double)(0);
|
|
}
|
|
if( (ae_fp_neq(ax,(double)(0))&&ae_fp_eq(ay,(double)(0)))&&ae_fp_eq(az,(double)(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 = (double)(1);
|
|
by = (double)(0);
|
|
az = (double)(1);
|
|
bz = (double)(0);
|
|
}
|
|
if( (ae_fp_eq(ax,(double)(0))&&ae_fp_eq(ay,(double)(0)))&&ae_fp_eq(az,(double)(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 = (double)(1);
|
|
bx = (double)(0);
|
|
ay = (double)(1);
|
|
by = (double)(0);
|
|
az = (double)(1);
|
|
bz = (double)(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);
|
|
memset(&x, 0, sizeof(x));
|
|
memset(&y, 0, sizeof(y));
|
|
memset(&z, 0, sizeof(z));
|
|
memset(&f, 0, sizeof(f));
|
|
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] = (double)(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, (double)(ci), _state)*ae_pow(dv, (double)(cj), _state)*ae_pow(dw, (double)(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 = (double)(0);
|
|
*fx = (double)(0);
|
|
*fy = (double)(0);
|
|
*fxy = (double)(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;
|
|
}
|
|
}
|
|
|
|
|
|
void _spline3dinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline3dinterpolant *p = (spline3dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->z, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->f, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _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;
|
|
ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->z, &src->z, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->f, &src->f, _state, make_automatic);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_POLINT) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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);
|
|
memset(&vp, 0, sizeof(vp));
|
|
memset(&vx, 0, sizeof(vx));
|
|
memset(&tk, 0, sizeof(tk));
|
|
memset(&tk1, 0, sizeof(tk1));
|
|
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 = (double)(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] = (double)(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);
|
|
memset(&y, 0, sizeof(y));
|
|
_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 = (double)(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;
|
|
double c0;
|
|
double s0;
|
|
double va;
|
|
double vb;
|
|
ae_vector vai;
|
|
ae_vector vbi;
|
|
double minx;
|
|
double maxx;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&vp, 0, sizeof(vp));
|
|
memset(&vx, 0, sizeof(vx));
|
|
memset(&tk, 0, sizeof(tk));
|
|
memset(&tk1, 0, sizeof(tk1));
|
|
memset(&t, 0, sizeof(t));
|
|
memset(&vai, 0, sizeof(vai));
|
|
memset(&vbi, 0, sizeof(vbi));
|
|
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_vector_init(&vai, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&vbi, 0, DT_REAL, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* We have barycentric model built using set of points X[], and we
|
|
* want to convert it to power basis centered about point C with
|
|
* scale S: I-th basis function is ((X-C)/S)^i.
|
|
*
|
|
* We use following three-stage algorithm:
|
|
*
|
|
* 1. we build Chebyshev representation of polynomial using
|
|
* intermediate center C0 and scale S0, which are derived from X[]:
|
|
* C0 = 0.5*(min(X)+max(X)), S0 = 0.5*(max(X)-min(X)). Chebyshev
|
|
* representation is built by sampling points around center C0,
|
|
* with typical distance between them proportional to S0.
|
|
* 2. then we transform form Chebyshev basis to intermediate power
|
|
* basis, using same center/scale C0/S0.
|
|
* 3. after that, we apply linear transformation to intermediate
|
|
* power basis which moves it to final center/scale C/S.
|
|
*
|
|
* The idea of such multi-stage algorithm is that it is much easier to
|
|
* transform barycentric model to Chebyshev basis, and only later to
|
|
* power basis, than transforming it directly to power basis. It is
|
|
* also more numerically stable to sample points using intermediate C0/S0,
|
|
* which are derived from user-supplied model, than using "final" C/S,
|
|
* which may be unsuitable for sampling (say, if S=1, we may have stability
|
|
* problems when working with models built from dataset with non-unit
|
|
* scale of abscissas).
|
|
*/
|
|
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,(double)(0)), "PolynomialBar2Pow: S=0!", _state);
|
|
ae_assert(p->n>0, "PolynomialBar2Pow: P is not correctly initialized barycentric interpolant!", _state);
|
|
|
|
/*
|
|
* Select intermediate center/scale
|
|
*/
|
|
minx = p->x.ptr.p_double[0];
|
|
maxx = p->x.ptr.p_double[0];
|
|
for(i=1; i<=p->n-1; i++)
|
|
{
|
|
minx = ae_minreal(minx, p->x.ptr.p_double[i], _state);
|
|
maxx = ae_maxreal(maxx, p->x.ptr.p_double[i], _state);
|
|
}
|
|
if( ae_fp_eq(minx,maxx) )
|
|
{
|
|
c0 = minx;
|
|
s0 = 1.0;
|
|
}
|
|
else
|
|
{
|
|
c0 = 0.5*(maxx+minx);
|
|
s0 = 0.5*(maxx-minx);
|
|
}
|
|
|
|
/*
|
|
* Calculate function values on a Chebyshev grid using intermediate C0/S0
|
|
*/
|
|
ae_vector_set_length(&vp, p->n+1, _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, s0*vx.ptr.p_double[i]+c0, _state);
|
|
}
|
|
|
|
/*
|
|
* T[0]
|
|
*/
|
|
ae_vector_set_length(&t, p->n, _state);
|
|
v = (double)(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] = (double)(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] = (double)(0);
|
|
}
|
|
d = (double)(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] = (double)(0);
|
|
if( i<=1&&k==i )
|
|
{
|
|
a->ptr.p_double[k] = (double)(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 = (double)(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;
|
|
}
|
|
|
|
/*
|
|
* Apply linear transformation which converts basis from intermediate
|
|
* one Fi=((x-C0)/S0)^i to final one Fi=((x-C)/S)^i.
|
|
*
|
|
* We have y=(x-C0)/S0, z=(x-C)/S, and coefficients A[] for basis Fi(y).
|
|
* Because we have y=A*z+B, for A=s/s0 and B=c/s0-c0/s0, we can perform
|
|
* substitution and get coefficients A_new[] in basis Fi(z).
|
|
*/
|
|
ae_assert(vp.cnt>=p->n+1, "PolynomialBar2Pow: internal error", _state);
|
|
ae_assert(t.cnt>=p->n, "PolynomialBar2Pow: internal error", _state);
|
|
for(i=0; i<=p->n-1; i++)
|
|
{
|
|
t.ptr.p_double[i] = 0.0;
|
|
}
|
|
va = s/s0;
|
|
vb = c/s0-c0/s0;
|
|
ae_vector_set_length(&vai, p->n, _state);
|
|
ae_vector_set_length(&vbi, p->n, _state);
|
|
vai.ptr.p_double[0] = (double)(1);
|
|
vbi.ptr.p_double[0] = (double)(1);
|
|
for(k=1; k<=p->n-1; k++)
|
|
{
|
|
vai.ptr.p_double[k] = vai.ptr.p_double[k-1]*va;
|
|
vbi.ptr.p_double[k] = vbi.ptr.p_double[k-1]*vb;
|
|
}
|
|
for(k=0; k<=p->n-1; k++)
|
|
{
|
|
|
|
/*
|
|
* Generate set of binomial coefficients in VP[]
|
|
*/
|
|
if( k>0 )
|
|
{
|
|
vp.ptr.p_double[k] = (double)(1);
|
|
for(i=k-1; i>=1; i--)
|
|
{
|
|
vp.ptr.p_double[i] = vp.ptr.p_double[i]+vp.ptr.p_double[i-1];
|
|
}
|
|
vp.ptr.p_double[0] = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
vp.ptr.p_double[0] = (double)(1);
|
|
}
|
|
|
|
/*
|
|
* Update T[] with expansion of K-th basis function
|
|
*/
|
|
for(i=0; i<=k; i++)
|
|
{
|
|
t.ptr.p_double[i] = t.ptr.p_double[i]+a->ptr.p_double[k]*vai.ptr.p_double[i]*vbi.ptr.p_double[k-i]*vp.ptr.p_double[i];
|
|
}
|
|
}
|
|
for(k=0; k<=p->n-1; k++)
|
|
{
|
|
a->ptr.p_double[k] = t.ptr.p_double[k];
|
|
}
|
|
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);
|
|
memset(&y, 0, sizeof(y));
|
|
_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,(double)(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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&sortrbuf, 0, sizeof(sortrbuf));
|
|
memset(&sortrbuf2, 0, sizeof(sortrbuf2));
|
|
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] = (double)(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);
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&x, 0, sizeof(x));
|
|
_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] = (double)(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 = (double)(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);
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&x, 0, sizeof(x));
|
|
_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] = (double)(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 = (double)(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);
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&x, 0, sizeof(x));
|
|
_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] = (double)(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 = (double)(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,(double)(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 = (double)(0);
|
|
s2 = (double)(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,(double)(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 = (double)(0);
|
|
s2 = (double)(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,(double)(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 = (double)(0);
|
|
s2 = (double)(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;
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_LSFIT) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine fits piecewise linear curve to points with Ramer-Douglas-
|
|
Peucker algorithm, which stops after generating specified number of linear
|
|
sections.
|
|
|
|
IMPORTANT:
|
|
* it does NOT perform least-squares fitting; it builds curve, but this
|
|
curve does not minimize some least squares metric. See description of
|
|
RDP algorithm (say, in Wikipedia) for more details on WHAT is performed.
|
|
* this function does NOT work with parametric curves (i.e. curves which
|
|
can be represented as {X(t),Y(t)}. It works with curves which can be
|
|
represented as Y(X). Thus, it is impossible to model figures like
|
|
circles with this functions.
|
|
If you want to work with parametric curves, you should use
|
|
ParametricRDPFixed() function provided by "Parametric" subpackage of
|
|
"Interpolation" package.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array of X-coordinates:
|
|
* at least N elements
|
|
* can be unordered (points are automatically sorted)
|
|
* this function may accept non-distinct X (see below for
|
|
more information on handling of such inputs)
|
|
Y - array of Y-coordinates:
|
|
* at least N elements
|
|
N - number of elements in X/Y
|
|
M - desired number of sections:
|
|
* at most M sections are generated by this function
|
|
* less than M sections can be generated if we have N<M
|
|
(or some X are non-distinct).
|
|
|
|
OUTPUT PARAMETERS:
|
|
X2 - X-values of corner points for piecewise approximation,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
Y2 - Y-values of corner points,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
NSections- number of sections found by algorithm, NSections<=M,
|
|
NSections can be zero for degenerate datasets
|
|
(N<=1 or all X[] are non-distinct).
|
|
|
|
NOTE: X2/Y2 are ordered arrays, i.e. (X2[0],Y2[0]) is a first point of
|
|
curve, (X2[NSection-1],Y2[NSection-1]) is the last point.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lstfitpiecewiselinearrdpfixed(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_int_t m,
|
|
/* Real */ ae_vector* x2,
|
|
/* Real */ ae_vector* y2,
|
|
ae_int_t* nsections,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
ae_int_t k2;
|
|
ae_vector buf0;
|
|
ae_vector buf1;
|
|
ae_matrix sections;
|
|
ae_vector points;
|
|
double v;
|
|
ae_int_t worstidx;
|
|
double worsterror;
|
|
ae_int_t idx0;
|
|
ae_int_t idx1;
|
|
double e0;
|
|
double e1;
|
|
ae_vector heaperrors;
|
|
ae_vector heaptags;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&buf0, 0, sizeof(buf0));
|
|
memset(&buf1, 0, sizeof(buf1));
|
|
memset(§ions, 0, sizeof(sections));
|
|
memset(&points, 0, sizeof(points));
|
|
memset(&heaperrors, 0, sizeof(heaperrors));
|
|
memset(&heaptags, 0, sizeof(heaptags));
|
|
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(x2);
|
|
ae_vector_clear(y2);
|
|
*nsections = 0;
|
|
ae_vector_init(&buf0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&buf1, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(§ions, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&points, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&heaperrors, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&heaptags, 0, DT_INT, _state, ae_true);
|
|
|
|
ae_assert(n>=0, "LSTFitPiecewiseLinearRDPFixed: N<0", _state);
|
|
ae_assert(m>=1, "LSTFitPiecewiseLinearRDPFixed: M<1", _state);
|
|
ae_assert(x->cnt>=n, "LSTFitPiecewiseLinearRDPFixed: Length(X)<N", _state);
|
|
ae_assert(y->cnt>=n, "LSTFitPiecewiseLinearRDPFixed: Length(Y)<N", _state);
|
|
if( n<=1 )
|
|
{
|
|
*nsections = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Sort points.
|
|
* Handle possible ties (tied values are replaced by their mean)
|
|
*/
|
|
tagsortfastr(x, y, &buf0, &buf1, n, _state);
|
|
i = 0;
|
|
while(i<=n-1)
|
|
{
|
|
j = i+1;
|
|
v = y->ptr.p_double[i];
|
|
while(j<=n-1&&ae_fp_eq(x->ptr.p_double[j],x->ptr.p_double[i]))
|
|
{
|
|
v = v+y->ptr.p_double[j];
|
|
j = j+1;
|
|
}
|
|
v = v/(j-i);
|
|
for(k=i; k<=j-1; k++)
|
|
{
|
|
y->ptr.p_double[k] = v;
|
|
}
|
|
i = j;
|
|
}
|
|
|
|
/*
|
|
* Handle degenerate case x[0]=x[N-1]
|
|
*/
|
|
if( ae_fp_eq(x->ptr.p_double[n-1],x->ptr.p_double[0]) )
|
|
{
|
|
*nsections = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Prepare first section
|
|
*/
|
|
lsfit_rdpanalyzesection(x, y, 0, n-1, &worstidx, &worsterror, _state);
|
|
ae_matrix_set_length(§ions, m, 4, _state);
|
|
ae_vector_set_length(&heaperrors, m, _state);
|
|
ae_vector_set_length(&heaptags, m, _state);
|
|
*nsections = 1;
|
|
sections.ptr.pp_double[0][0] = (double)(0);
|
|
sections.ptr.pp_double[0][1] = (double)(n-1);
|
|
sections.ptr.pp_double[0][2] = (double)(worstidx);
|
|
sections.ptr.pp_double[0][3] = worsterror;
|
|
heaperrors.ptr.p_double[0] = worsterror;
|
|
heaptags.ptr.p_int[0] = 0;
|
|
ae_assert(ae_fp_eq(sections.ptr.pp_double[0][1],(double)(n-1)), "RDP algorithm: integrity check failed", _state);
|
|
|
|
/*
|
|
* Main loop.
|
|
* Repeatedly find section with worst error and divide it.
|
|
* Terminate after M-th section, or because of other reasons (see loop internals).
|
|
*/
|
|
while(*nsections<m)
|
|
{
|
|
|
|
/*
|
|
* Break if worst section has zero error.
|
|
* Store index of worst section to K.
|
|
*/
|
|
if( ae_fp_eq(heaperrors.ptr.p_double[0],(double)(0)) )
|
|
{
|
|
break;
|
|
}
|
|
k = heaptags.ptr.p_int[0];
|
|
|
|
/*
|
|
* K-th section is divided in two:
|
|
* * first one spans interval from X[Sections[K,0]] to X[Sections[K,2]]
|
|
* * second one spans interval from X[Sections[K,2]] to X[Sections[K,1]]
|
|
*
|
|
* First section is stored at K-th position, second one is appended to the table.
|
|
* Then we update heap which stores pairs of (error,section_index)
|
|
*/
|
|
k0 = ae_round(sections.ptr.pp_double[k][0], _state);
|
|
k1 = ae_round(sections.ptr.pp_double[k][1], _state);
|
|
k2 = ae_round(sections.ptr.pp_double[k][2], _state);
|
|
lsfit_rdpanalyzesection(x, y, k0, k2, &idx0, &e0, _state);
|
|
lsfit_rdpanalyzesection(x, y, k2, k1, &idx1, &e1, _state);
|
|
sections.ptr.pp_double[k][0] = (double)(k0);
|
|
sections.ptr.pp_double[k][1] = (double)(k2);
|
|
sections.ptr.pp_double[k][2] = (double)(idx0);
|
|
sections.ptr.pp_double[k][3] = e0;
|
|
tagheapreplacetopi(&heaperrors, &heaptags, *nsections, e0, k, _state);
|
|
sections.ptr.pp_double[*nsections][0] = (double)(k2);
|
|
sections.ptr.pp_double[*nsections][1] = (double)(k1);
|
|
sections.ptr.pp_double[*nsections][2] = (double)(idx1);
|
|
sections.ptr.pp_double[*nsections][3] = e1;
|
|
tagheappushi(&heaperrors, &heaptags, nsections, e1, *nsections, _state);
|
|
}
|
|
|
|
/*
|
|
* Convert from sections to points
|
|
*/
|
|
ae_vector_set_length(&points, *nsections+1, _state);
|
|
k = ae_round(sections.ptr.pp_double[0][1], _state);
|
|
for(i=0; i<=*nsections-1; i++)
|
|
{
|
|
points.ptr.p_double[i] = (double)(ae_round(sections.ptr.pp_double[i][0], _state));
|
|
if( ae_fp_greater(x->ptr.p_double[ae_round(sections.ptr.pp_double[i][1], _state)],x->ptr.p_double[k]) )
|
|
{
|
|
k = ae_round(sections.ptr.pp_double[i][1], _state);
|
|
}
|
|
}
|
|
points.ptr.p_double[*nsections] = (double)(k);
|
|
tagsortfast(&points, &buf0, *nsections+1, _state);
|
|
|
|
/*
|
|
* Output sections:
|
|
* * first NSection elements of X2/Y2 are filled by x/y at left boundaries of sections
|
|
* * last element of X2/Y2 is filled by right boundary of rightmost section
|
|
* * X2/Y2 is sorted by ascending of X2
|
|
*/
|
|
ae_vector_set_length(x2, *nsections+1, _state);
|
|
ae_vector_set_length(y2, *nsections+1, _state);
|
|
for(i=0; i<=*nsections; i++)
|
|
{
|
|
x2->ptr.p_double[i] = x->ptr.p_double[ae_round(points.ptr.p_double[i], _state)];
|
|
y2->ptr.p_double[i] = y->ptr.p_double[ae_round(points.ptr.p_double[i], _state)];
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine fits piecewise linear curve to points with Ramer-Douglas-
|
|
Peucker algorithm, which stops after achieving desired precision.
|
|
|
|
IMPORTANT:
|
|
* it performs non-least-squares fitting; it builds curve, but this curve
|
|
does not minimize some least squares metric. See description of RDP
|
|
algorithm (say, in Wikipedia) for more details on WHAT is performed.
|
|
* this function does NOT work with parametric curves (i.e. curves which
|
|
can be represented as {X(t),Y(t)}. It works with curves which can be
|
|
represented as Y(X). Thus, it is impossible to model figures like circles
|
|
with this functions.
|
|
If you want to work with parametric curves, you should use
|
|
ParametricRDPFixed() function provided by "Parametric" subpackage of
|
|
"Interpolation" package.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array of X-coordinates:
|
|
* at least N elements
|
|
* can be unordered (points are automatically sorted)
|
|
* this function may accept non-distinct X (see below for
|
|
more information on handling of such inputs)
|
|
Y - array of Y-coordinates:
|
|
* at least N elements
|
|
N - number of elements in X/Y
|
|
Eps - positive number, desired precision.
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
X2 - X-values of corner points for piecewise approximation,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
Y2 - Y-values of corner points,
|
|
has length NSections+1 or zero (for NSections=0).
|
|
NSections- number of sections found by algorithm,
|
|
NSections can be zero for degenerate datasets
|
|
(N<=1 or all X[] are non-distinct).
|
|
|
|
NOTE: X2/Y2 are ordered arrays, i.e. (X2[0],Y2[0]) is a first point of
|
|
curve, (X2[NSection-1],Y2[NSection-1]) is the last point.
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lstfitpiecewiselinearrdp(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double eps,
|
|
/* Real */ ae_vector* x2,
|
|
/* Real */ ae_vector* y2,
|
|
ae_int_t* nsections,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_vector buf0;
|
|
ae_vector buf1;
|
|
ae_vector xtmp;
|
|
ae_vector ytmp;
|
|
double v;
|
|
ae_int_t npts;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&buf0, 0, sizeof(buf0));
|
|
memset(&buf1, 0, sizeof(buf1));
|
|
memset(&xtmp, 0, sizeof(xtmp));
|
|
memset(&ytmp, 0, sizeof(ytmp));
|
|
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(x2);
|
|
ae_vector_clear(y2);
|
|
*nsections = 0;
|
|
ae_vector_init(&buf0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&buf1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xtmp, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ytmp, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n>=0, "LSTFitPiecewiseLinearRDP: N<0", _state);
|
|
ae_assert(ae_fp_greater(eps,(double)(0)), "LSTFitPiecewiseLinearRDP: Eps<=0", _state);
|
|
ae_assert(x->cnt>=n, "LSTFitPiecewiseLinearRDP: Length(X)<N", _state);
|
|
ae_assert(y->cnt>=n, "LSTFitPiecewiseLinearRDP: Length(Y)<N", _state);
|
|
if( n<=1 )
|
|
{
|
|
*nsections = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Sort points.
|
|
* Handle possible ties (tied values are replaced by their mean)
|
|
*/
|
|
tagsortfastr(x, y, &buf0, &buf1, n, _state);
|
|
i = 0;
|
|
while(i<=n-1)
|
|
{
|
|
j = i+1;
|
|
v = y->ptr.p_double[i];
|
|
while(j<=n-1&&ae_fp_eq(x->ptr.p_double[j],x->ptr.p_double[i]))
|
|
{
|
|
v = v+y->ptr.p_double[j];
|
|
j = j+1;
|
|
}
|
|
v = v/(j-i);
|
|
for(k=i; k<=j-1; k++)
|
|
{
|
|
y->ptr.p_double[k] = v;
|
|
}
|
|
i = j;
|
|
}
|
|
|
|
/*
|
|
* Handle degenerate case x[0]=x[N-1]
|
|
*/
|
|
if( ae_fp_eq(x->ptr.p_double[n-1],x->ptr.p_double[0]) )
|
|
{
|
|
*nsections = 0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Prepare data for recursive algorithm
|
|
*/
|
|
ae_vector_set_length(&xtmp, n, _state);
|
|
ae_vector_set_length(&ytmp, n, _state);
|
|
npts = 2;
|
|
xtmp.ptr.p_double[0] = x->ptr.p_double[0];
|
|
ytmp.ptr.p_double[0] = y->ptr.p_double[0];
|
|
xtmp.ptr.p_double[1] = x->ptr.p_double[n-1];
|
|
ytmp.ptr.p_double[1] = y->ptr.p_double[n-1];
|
|
lsfit_rdprecursive(x, y, 0, n-1, eps, &xtmp, &ytmp, &npts, _state);
|
|
|
|
/*
|
|
* Output sections:
|
|
* * first NSection elements of X2/Y2 are filled by x/y at left boundaries of sections
|
|
* * last element of X2/Y2 is filled by right boundary of rightmost section
|
|
* * X2/Y2 is sorted by ascending of X2
|
|
*/
|
|
*nsections = npts-1;
|
|
ae_vector_set_length(x2, npts, _state);
|
|
ae_vector_set_length(y2, npts, _state);
|
|
for(i=0; i<=*nsections; i++)
|
|
{
|
|
x2->ptr.p_double[i] = xtmp.ptr.p_double[i];
|
|
y2->ptr.p_double[i] = ytmp.ptr.p_double[i];
|
|
}
|
|
tagsortfastr(x2, y2, &buf0, &buf1, npts, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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()
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
|
|
-- 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);
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&xc, 0, sizeof(xc));
|
|
memset(&yc, 0, sizeof(yc));
|
|
memset(&dc, 0, sizeof(dc));
|
|
*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] = (double)(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()
|
|
|
|
NOTES:
|
|
you can convert P from barycentric form to the power or Chebyshev
|
|
basis with PolynomialBar2Pow() or PolynomialBar2Cheb() functions from
|
|
POLINT subpackage.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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.
|
|
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_w, 0, sizeof(_w));
|
|
memset(&_xc, 0, sizeof(_xc));
|
|
memset(&_yc, 0, sizeof(_yc));
|
|
memset(&xoriginal, 0, sizeof(xoriginal));
|
|
memset(&yoriginal, 0, sizeof(yoriginal));
|
|
memset(&y2, 0, sizeof(y2));
|
|
memset(&w2, 0, sizeof(w2));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memset(&tmp2, 0, sizeof(tmp2));
|
|
memset(&bx, 0, sizeof(bx));
|
|
memset(&by, 0, sizeof(by));
|
|
memset(&bw, 0, sizeof(bw));
|
|
memset(&lrep, 0, sizeof(lrep));
|
|
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 = (double)(1);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
if( m!=1 )
|
|
{
|
|
u = ae_cos(ae_pi*i/(m-1), _state);
|
|
}
|
|
else
|
|
{
|
|
u = (double)(0);
|
|
}
|
|
v = (double)(0);
|
|
for(j=0; j<=m-1; j++)
|
|
{
|
|
if( j==0 )
|
|
{
|
|
tmp2.ptr.p_double[j] = (double)(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 = (double)(0);
|
|
relcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(yoriginal.ptr.p_double[i],(double)(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);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates value of four-parameter logistic (4PL) model at
|
|
specified point X. 4PL model has following form:
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B))
|
|
|
|
INPUT PARAMETERS:
|
|
X - current point, X>=0:
|
|
* zero X is correctly handled even for B<=0
|
|
* negative X results in exception.
|
|
A, B, C, D- parameters of 4PL model:
|
|
* A is unconstrained
|
|
* B is unconstrained; zero or negative values are handled
|
|
correctly.
|
|
* C>0, non-positive value results in exception
|
|
* D is unconstrained
|
|
|
|
RESULT:
|
|
model value at X
|
|
|
|
NOTE: if B=0, denominator is assumed to be equal to 2.0 even for zero X
|
|
(strictly speaking, 0^0 is undefined).
|
|
|
|
NOTE: this function also throws exception if all input parameters are
|
|
correct, but overflow was detected during calculations.
|
|
|
|
NOTE: this function performs a lot of checks; if you need really high
|
|
performance, consider evaluating model yourself, without checking
|
|
for degenerate cases.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.05.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double logisticcalc4(double x,
|
|
double a,
|
|
double b,
|
|
double c,
|
|
double d,
|
|
ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
ae_assert(ae_isfinite(x, _state), "LogisticCalc4: X is not finite", _state);
|
|
ae_assert(ae_isfinite(a, _state), "LogisticCalc4: A is not finite", _state);
|
|
ae_assert(ae_isfinite(b, _state), "LogisticCalc4: B is not finite", _state);
|
|
ae_assert(ae_isfinite(c, _state), "LogisticCalc4: C is not finite", _state);
|
|
ae_assert(ae_isfinite(d, _state), "LogisticCalc4: D is not finite", _state);
|
|
ae_assert(ae_fp_greater_eq(x,(double)(0)), "LogisticCalc4: X is negative", _state);
|
|
ae_assert(ae_fp_greater(c,(double)(0)), "LogisticCalc4: C is non-positive", _state);
|
|
|
|
/*
|
|
* Check for degenerate cases
|
|
*/
|
|
if( ae_fp_eq(b,(double)(0)) )
|
|
{
|
|
result = 0.5*(a+d);
|
|
return result;
|
|
}
|
|
if( ae_fp_eq(x,(double)(0)) )
|
|
{
|
|
if( ae_fp_greater(b,(double)(0)) )
|
|
{
|
|
result = a;
|
|
}
|
|
else
|
|
{
|
|
result = d;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* General case
|
|
*/
|
|
result = d+(a-d)/(1.0+ae_pow(x/c, b, _state));
|
|
ae_assert(ae_isfinite(result, _state), "LogisticCalc4: overflow during calculations", _state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates value of five-parameter logistic (5PL) model at
|
|
specified point X. 5PL model has following form:
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G)
|
|
|
|
INPUT PARAMETERS:
|
|
X - current point, X>=0:
|
|
* zero X is correctly handled even for B<=0
|
|
* negative X results in exception.
|
|
A, B, C, D, G- parameters of 5PL model:
|
|
* A is unconstrained
|
|
* B is unconstrained; zero or negative values are handled
|
|
correctly.
|
|
* C>0, non-positive value results in exception
|
|
* D is unconstrained
|
|
* G>0, non-positive value results in exception
|
|
|
|
RESULT:
|
|
model value at X
|
|
|
|
NOTE: if B=0, denominator is assumed to be equal to Power(2.0,G) even for
|
|
zero X (strictly speaking, 0^0 is undefined).
|
|
|
|
NOTE: this function also throws exception if all input parameters are
|
|
correct, but overflow was detected during calculations.
|
|
|
|
NOTE: this function performs a lot of checks; if you need really high
|
|
performance, consider evaluating model yourself, without checking
|
|
for degenerate cases.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.05.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double logisticcalc5(double x,
|
|
double a,
|
|
double b,
|
|
double c,
|
|
double d,
|
|
double g,
|
|
ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
ae_assert(ae_isfinite(x, _state), "LogisticCalc5: X is not finite", _state);
|
|
ae_assert(ae_isfinite(a, _state), "LogisticCalc5: A is not finite", _state);
|
|
ae_assert(ae_isfinite(b, _state), "LogisticCalc5: B is not finite", _state);
|
|
ae_assert(ae_isfinite(c, _state), "LogisticCalc5: C is not finite", _state);
|
|
ae_assert(ae_isfinite(d, _state), "LogisticCalc5: D is not finite", _state);
|
|
ae_assert(ae_isfinite(g, _state), "LogisticCalc5: G is not finite", _state);
|
|
ae_assert(ae_fp_greater_eq(x,(double)(0)), "LogisticCalc5: X is negative", _state);
|
|
ae_assert(ae_fp_greater(c,(double)(0)), "LogisticCalc5: C is non-positive", _state);
|
|
ae_assert(ae_fp_greater(g,(double)(0)), "LogisticCalc5: G is non-positive", _state);
|
|
|
|
/*
|
|
* Check for degenerate cases
|
|
*/
|
|
if( ae_fp_eq(b,(double)(0)) )
|
|
{
|
|
result = d+(a-d)/ae_pow(2.0, g, _state);
|
|
return result;
|
|
}
|
|
if( ae_fp_eq(x,(double)(0)) )
|
|
{
|
|
if( ae_fp_greater(b,(double)(0)) )
|
|
{
|
|
result = a;
|
|
}
|
|
else
|
|
{
|
|
result = d;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* General case
|
|
*/
|
|
result = d+(a-d)/ae_pow(1.0+ae_pow(x/c, b, _state), g, _state);
|
|
ae_assert(ae_isfinite(result, _state), "LogisticCalc5: overflow during calculations", _state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function fits four-parameter logistic (4PL) model to data provided
|
|
by user. 4PL model has following form:
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B))
|
|
|
|
Here:
|
|
* A, D - unconstrained (see LogisticFit4EC() for constrained 4PL)
|
|
* B>=0
|
|
* C>0
|
|
|
|
IMPORTANT: output of this function is constrained in such way that B>0.
|
|
Because 4PL model is symmetric with respect to B, there is no
|
|
need to explore B<0. Constraining B makes algorithm easier
|
|
to stabilize and debug.
|
|
Users who for some reason prefer to work with negative B's
|
|
should transform output themselves (swap A and D, replace B by
|
|
-B).
|
|
|
|
4PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot".
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints. Results from the previous round are used as initial guess.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A, B, C, D- parameters of 4PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for stability reasons the B parameter is restricted by [1/1000,1000]
|
|
range. It prevents algorithm from making trial steps deep into the
|
|
area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc4() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit4(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double* a,
|
|
double* b,
|
|
double* c,
|
|
double* d,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
double g;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
*a = 0;
|
|
*b = 0;
|
|
*c = 0;
|
|
*d = 0;
|
|
_lsfitreport_clear(rep);
|
|
|
|
logisticfit45x(x, y, n, _state->v_nan, _state->v_nan, ae_true, 0.0, 0.0, 0, a, b, c, d, &g, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function fits four-parameter logistic (4PL) model to data provided
|
|
by user, with optional constraints on parameters A and D. 4PL model has
|
|
following form:
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B))
|
|
|
|
Here:
|
|
* A, D - with optional equality constraints
|
|
* B>=0
|
|
* C>0
|
|
|
|
IMPORTANT: output of this function is constrained in such way that B>0.
|
|
Because 4PL model is symmetric with respect to B, there is no
|
|
need to explore B<0. Constraining B makes algorithm easier
|
|
to stabilize and debug.
|
|
Users who for some reason prefer to work with negative B's
|
|
should transform output themselves (swap A and D, replace B by
|
|
-B).
|
|
|
|
4PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot".
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints. Results from the previous round are used as initial guess.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
CnstrLeft- optional equality constraint for model value at the left
|
|
boundary (at X=0). Specify NAN (Not-a-Number) if you do
|
|
not need constraint on the model value at X=0 (in C++ you
|
|
can pass alglib::fp_nan as parameter, in C# it will be
|
|
Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
CnstrRight- optional equality constraint for model value at X=infinity.
|
|
Specify NAN (Not-a-Number) if you do not need constraint
|
|
on the model value (in C++ you can pass alglib::fp_nan as
|
|
parameter, in C# it will be Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A, B, C, D- parameters of 4PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for stability reasons the B parameter is restricted by [1/1000,1000]
|
|
range. It prevents algorithm from making trial steps deep into the
|
|
area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc4() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
EQUALITY CONSTRAINTS ON PARAMETERS
|
|
|
|
4PL/5PL solver supports equality constraints on model values at the left
|
|
boundary (X=0) and right boundary (X=infinity). These constraints are
|
|
completely optional and you can specify both of them, only one - or no
|
|
constraints at all.
|
|
|
|
Parameter CnstrLeft contains left constraint (or NAN for unconstrained
|
|
fitting), and CnstrRight contains right one. For 4PL, left constraint
|
|
ALWAYS corresponds to parameter A, and right one is ALWAYS constraint on
|
|
D. That's because 4PL model is normalized in such way that B>=0.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit4ec(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double cnstrleft,
|
|
double cnstrright,
|
|
double* a,
|
|
double* b,
|
|
double* c,
|
|
double* d,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
double g;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
*a = 0;
|
|
*b = 0;
|
|
*c = 0;
|
|
*d = 0;
|
|
_lsfitreport_clear(rep);
|
|
|
|
logisticfit45x(x, y, n, cnstrleft, cnstrright, ae_true, 0.0, 0.0, 0, a, b, c, d, &g, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function fits five-parameter logistic (5PL) model to data provided
|
|
by user. 5PL model has following form:
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G)
|
|
|
|
Here:
|
|
* A, D - unconstrained
|
|
* B - unconstrained
|
|
* C>0
|
|
* G>0
|
|
|
|
IMPORTANT: unlike in 4PL fitting, output of this function is NOT
|
|
constrained in such way that B is guaranteed to be positive.
|
|
Furthermore, unlike 4PL, 5PL model is NOT symmetric with
|
|
respect to B, so you can NOT transform model to equivalent one,
|
|
with B having desired sign (>0 or <0).
|
|
|
|
5PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot". Parameter G is
|
|
fixed at G=1.
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints on B and C, but with G still equal to 1. Results from the
|
|
previous round are used as initial guess.
|
|
* third Levenberg-Marquardt round relaxes constraints on G and tries two
|
|
different models - one with B>0 and one with B<0.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A,B,C,D,G- parameters of 5PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for better stability B parameter is restricted by [+-1/1000,+-1000]
|
|
range, and G is restricted by [1/10,10] range. It prevents algorithm
|
|
from making trial steps deep into the area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc5() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit5(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double* a,
|
|
double* b,
|
|
double* c,
|
|
double* d,
|
|
double* g,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
*a = 0;
|
|
*b = 0;
|
|
*c = 0;
|
|
*d = 0;
|
|
*g = 0;
|
|
_lsfitreport_clear(rep);
|
|
|
|
logisticfit45x(x, y, n, _state->v_nan, _state->v_nan, ae_false, 0.0, 0.0, 0, a, b, c, d, g, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function fits five-parameter logistic (5PL) model to data provided
|
|
by user, subject to optional equality constraints on parameters A and D.
|
|
5PL model has following form:
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G)
|
|
|
|
Here:
|
|
* A, D - with optional equality constraints
|
|
* B - unconstrained
|
|
* C>0
|
|
* G>0
|
|
|
|
IMPORTANT: unlike in 4PL fitting, output of this function is NOT
|
|
constrained in such way that B is guaranteed to be positive.
|
|
Furthermore, unlike 4PL, 5PL model is NOT symmetric with
|
|
respect to B, so you can NOT transform model to equivalent one,
|
|
with B having desired sign (>0 or <0).
|
|
|
|
5PL fitting is implemented as follows:
|
|
* we perform small number of restarts from random locations which helps to
|
|
solve problem of bad local extrema. Locations are only partially random
|
|
- we use input data to determine good initial guess, but we include
|
|
controlled amount of randomness.
|
|
* we perform Levenberg-Marquardt fitting with very tight constraints on
|
|
parameters B and C - it allows us to find good initial guess for the
|
|
second stage without risk of running into "flat spot". Parameter G is
|
|
fixed at G=1.
|
|
* second Levenberg-Marquardt round is performed without excessive
|
|
constraints on B and C, but with G still equal to 1. Results from the
|
|
previous round are used as initial guess.
|
|
* third Levenberg-Marquardt round relaxes constraints on G and tries two
|
|
different models - one with B>0 and one with B<0.
|
|
* after fitting is done, we compare results with best values found so far,
|
|
rewrite "best solution" if needed, and move to next random location.
|
|
|
|
Overall algorithm is very stable and is not prone to bad local extrema.
|
|
Furthermore, it automatically scales when input data have very large or
|
|
very small range.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
CnstrLeft- optional equality constraint for model value at the left
|
|
boundary (at X=0). Specify NAN (Not-a-Number) if you do
|
|
not need constraint on the model value at X=0 (in C++ you
|
|
can pass alglib::fp_nan as parameter, in C# it will be
|
|
Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
CnstrRight- optional equality constraint for model value at X=infinity.
|
|
Specify NAN (Not-a-Number) if you do not need constraint
|
|
on the model value (in C++ you can pass alglib::fp_nan as
|
|
parameter, in C# it will be Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
|
|
OUTPUT PARAMETERS:
|
|
A,B,C,D,G- parameters of 5PL model
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for better stability B parameter is restricted by [+-1/1000,+-1000]
|
|
range, and G is restricted by [1/10,10] range. It prevents algorithm
|
|
from making trial steps deep into the area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc5() function.
|
|
|
|
NOTE: if you need better control over fitting process than provided by this
|
|
function, you may use LogisticFit45X().
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
EQUALITY CONSTRAINTS ON PARAMETERS
|
|
|
|
5PL solver supports equality constraints on model values at the left
|
|
boundary (X=0) and right boundary (X=infinity). These constraints are
|
|
completely optional and you can specify both of them, only one - or no
|
|
constraints at all.
|
|
|
|
Parameter CnstrLeft contains left constraint (or NAN for unconstrained
|
|
fitting), and CnstrRight contains right one.
|
|
|
|
Unlike 4PL one, 5PL model is NOT symmetric with respect to change in sign
|
|
of B. Thus, negative B's are possible, and left constraint may constrain
|
|
parameter A (for positive B's) - or parameter D (for negative B's).
|
|
Similarly changes meaning of right constraint.
|
|
|
|
You do not have to decide what parameter to constrain - algorithm will
|
|
automatically determine correct parameters as fitting progresses. However,
|
|
question highlighted above is important when you interpret fitting results.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit5ec(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double cnstrleft,
|
|
double cnstrright,
|
|
double* a,
|
|
double* b,
|
|
double* c,
|
|
double* d,
|
|
double* g,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
*a = 0;
|
|
*b = 0;
|
|
*c = 0;
|
|
*d = 0;
|
|
*g = 0;
|
|
_lsfitreport_clear(rep);
|
|
|
|
logisticfit45x(x, y, n, cnstrleft, cnstrright, ae_false, 0.0, 0.0, 0, a, b, c, d, g, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This is "expert" 4PL/5PL fitting function, which can be used if you need
|
|
better control over fitting process than provided by LogisticFit4() or
|
|
LogisticFit5().
|
|
|
|
This function fits model of the form
|
|
|
|
F(x|A,B,C,D) = D+(A-D)/(1+Power(x/C,B)) (4PL model)
|
|
|
|
or
|
|
|
|
F(x|A,B,C,D,G) = D+(A-D)/Power(1+Power(x/C,B),G) (5PL model)
|
|
|
|
Here:
|
|
* A, D - unconstrained
|
|
* B>=0 for 4PL, unconstrained for 5PL
|
|
* C>0
|
|
* G>0 (if present)
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N], stores X-values.
|
|
MUST include only non-negative numbers (but may include
|
|
zero values). Can be unsorted.
|
|
Y - array[N], values to fit.
|
|
N - number of points. If N is less than length of X/Y, only
|
|
leading N elements are used.
|
|
CnstrLeft- optional equality constraint for model value at the left
|
|
boundary (at X=0). Specify NAN (Not-a-Number) if you do
|
|
not need constraint on the model value at X=0 (in C++ you
|
|
can pass alglib::fp_nan as parameter, in C# it will be
|
|
Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
CnstrRight- optional equality constraint for model value at X=infinity.
|
|
Specify NAN (Not-a-Number) if you do not need constraint
|
|
on the model value (in C++ you can pass alglib::fp_nan as
|
|
parameter, in C# it will be Double.NaN).
|
|
See below, section "EQUALITY CONSTRAINTS" for more
|
|
information about constraints.
|
|
Is4PL - whether 4PL or 5PL models are fitted
|
|
LambdaV - regularization coefficient, LambdaV>=0.
|
|
Set it to zero unless you know what you are doing.
|
|
EpsX - stopping condition (step size), EpsX>=0.
|
|
Zero value means that small step is automatically chosen.
|
|
See notes below for more information.
|
|
RsCnt - number of repeated restarts from random points. 4PL/5PL
|
|
models are prone to problem of bad local extrema. Utilizing
|
|
multiple random restarts allows us to improve algorithm
|
|
convergence.
|
|
RsCnt>=0.
|
|
Zero value means that function automatically choose small
|
|
amount of restarts (recommended).
|
|
|
|
OUTPUT PARAMETERS:
|
|
A, B, C, D- parameters of 4PL model
|
|
G - parameter of 5PL model; for Is4PL=True, G=1 is returned.
|
|
Rep - fitting report. This structure has many fields, but ONLY
|
|
ONES LISTED BELOW ARE SET:
|
|
* Rep.IterationsCount - number of iterations performed
|
|
* Rep.RMSError - root-mean-square error
|
|
* Rep.AvgError - average absolute error
|
|
* Rep.AvgRelError - average relative error (calculated for
|
|
non-zero Y-values)
|
|
* Rep.MaxError - maximum absolute error
|
|
* Rep.R2 - coefficient of determination, R-squared. This
|
|
coefficient is calculated as R2=1-RSS/TSS (in case
|
|
of nonlinear regression there are multiple ways to
|
|
define R2, each of them giving different results).
|
|
|
|
NOTE: for better stability B parameter is restricted by [+-1/1000,+-1000]
|
|
range, and G is restricted by [1/10,10] range. It prevents algorithm
|
|
from making trial steps deep into the area of bad parameters.
|
|
|
|
NOTE: after you obtained coefficients, you can evaluate model with
|
|
LogisticCalc5() function.
|
|
|
|
NOTE: step is automatically scaled according to scale of parameters being
|
|
fitted before we compare its length with EpsX. Thus, this function
|
|
can be used to fit data with very small or very large values without
|
|
changing EpsX.
|
|
|
|
EQUALITY CONSTRAINTS ON PARAMETERS
|
|
|
|
4PL/5PL solver supports equality constraints on model values at the left
|
|
boundary (X=0) and right boundary (X=infinity). These constraints are
|
|
completely optional and you can specify both of them, only one - or no
|
|
constraints at all.
|
|
|
|
Parameter CnstrLeft contains left constraint (or NAN for unconstrained
|
|
fitting), and CnstrRight contains right one. For 4PL, left constraint
|
|
ALWAYS corresponds to parameter A, and right one is ALWAYS constraint on
|
|
D. That's because 4PL model is normalized in such way that B>=0.
|
|
|
|
For 5PL model things are different. Unlike 4PL one, 5PL model is NOT
|
|
symmetric with respect to change in sign of B. Thus, negative B's are
|
|
possible, and left constraint may constrain parameter A (for positive B's)
|
|
- or parameter D (for negative B's). Similarly changes meaning of right
|
|
constraint.
|
|
|
|
You do not have to decide what parameter to constrain - algorithm will
|
|
automatically determine correct parameters as fitting progresses. However,
|
|
question highlighted above is important when you interpret fitting results.
|
|
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 14.02.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void logisticfit45x(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double cnstrleft,
|
|
double cnstrright,
|
|
ae_bool is4pl,
|
|
double lambdav,
|
|
double epsx,
|
|
ae_int_t rscnt,
|
|
double* a,
|
|
double* b,
|
|
double* c,
|
|
double* d,
|
|
double* g,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector _x;
|
|
ae_vector _y;
|
|
ae_int_t i;
|
|
ae_int_t outerit;
|
|
ae_int_t nz;
|
|
double v;
|
|
ae_vector p0;
|
|
ae_vector p1;
|
|
ae_vector p2;
|
|
ae_vector bndl;
|
|
ae_vector bndu;
|
|
ae_vector s;
|
|
ae_vector bndl1;
|
|
ae_vector bndu1;
|
|
ae_vector bndl2;
|
|
ae_vector bndu2;
|
|
ae_matrix z;
|
|
hqrndstate rs;
|
|
minlmstate state;
|
|
minlmreport replm;
|
|
ae_int_t maxits;
|
|
double fbest;
|
|
double flast;
|
|
double scalex;
|
|
double scaley;
|
|
ae_vector bufx;
|
|
ae_vector bufy;
|
|
double fposb;
|
|
double fnegb;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&p0, 0, sizeof(p0));
|
|
memset(&p1, 0, sizeof(p1));
|
|
memset(&p2, 0, sizeof(p2));
|
|
memset(&bndl, 0, sizeof(bndl));
|
|
memset(&bndu, 0, sizeof(bndu));
|
|
memset(&s, 0, sizeof(s));
|
|
memset(&bndl1, 0, sizeof(bndl1));
|
|
memset(&bndu1, 0, sizeof(bndu1));
|
|
memset(&bndl2, 0, sizeof(bndl2));
|
|
memset(&bndu2, 0, sizeof(bndu2));
|
|
memset(&z, 0, sizeof(z));
|
|
memset(&rs, 0, sizeof(rs));
|
|
memset(&state, 0, sizeof(state));
|
|
memset(&replm, 0, sizeof(replm));
|
|
memset(&bufx, 0, sizeof(bufx));
|
|
memset(&bufy, 0, sizeof(bufy));
|
|
ae_vector_init_copy(&_x, x, _state, ae_true);
|
|
x = &_x;
|
|
ae_vector_init_copy(&_y, y, _state, ae_true);
|
|
y = &_y;
|
|
*a = 0;
|
|
*b = 0;
|
|
*c = 0;
|
|
*d = 0;
|
|
*g = 0;
|
|
_lsfitreport_clear(rep);
|
|
ae_vector_init(&p0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&p2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bndl, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bndu, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&s, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bndl1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bndu1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bndl2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bndu2, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&z, 0, 0, DT_REAL, _state, ae_true);
|
|
_hqrndstate_init(&rs, _state, ae_true);
|
|
_minlmstate_init(&state, _state, ae_true);
|
|
_minlmreport_init(&replm, _state, ae_true);
|
|
ae_vector_init(&bufx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bufy, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(ae_isfinite(epsx, _state), "LogisticFitX: EpsX is infinite/NAN", _state);
|
|
ae_assert(ae_isfinite(lambdav, _state), "LogisticFitX: LambdaV is infinite/NAN", _state);
|
|
ae_assert(ae_isfinite(cnstrleft, _state)||ae_isnan(cnstrleft, _state), "LogisticFitX: CnstrLeft is NOT finite or NAN", _state);
|
|
ae_assert(ae_isfinite(cnstrright, _state)||ae_isnan(cnstrright, _state), "LogisticFitX: CnstrRight is NOT finite or NAN", _state);
|
|
ae_assert(ae_fp_greater_eq(lambdav,(double)(0)), "LogisticFitX: negative LambdaV", _state);
|
|
ae_assert(n>0, "LogisticFitX: N<=0", _state);
|
|
ae_assert(rscnt>=0, "LogisticFitX: RsCnt<0", _state);
|
|
ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "LogisticFitX: EpsX<0", _state);
|
|
ae_assert(x->cnt>=n, "LogisticFitX: Length(X)<N", _state);
|
|
ae_assert(y->cnt>=n, "LogisticFitX: Length(Y)<N", _state);
|
|
ae_assert(isfinitevector(x, n, _state), "LogisticFitX: X contains infinite/NAN values", _state);
|
|
ae_assert(isfinitevector(y, n, _state), "LogisticFitX: X contains infinite/NAN values", _state);
|
|
hqrndseed(2211, 1033044, &rs, _state);
|
|
lsfit_clearreport(rep, _state);
|
|
if( ae_fp_eq(epsx,(double)(0)) )
|
|
{
|
|
epsx = 1.0E-10;
|
|
}
|
|
if( rscnt==0 )
|
|
{
|
|
rscnt = 4;
|
|
}
|
|
maxits = 1000;
|
|
|
|
/*
|
|
* Sort points by X.
|
|
* Determine number of zero and non-zero values.
|
|
*/
|
|
tagsortfastr(x, y, &bufx, &bufy, n, _state);
|
|
ae_assert(ae_fp_greater_eq(x->ptr.p_double[0],(double)(0)), "LogisticFitX: some X[] are negative", _state);
|
|
nz = n;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_greater(x->ptr.p_double[i],(double)(0)) )
|
|
{
|
|
nz = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* For NZ=N (all X[] are zero) special code is used.
|
|
* For NZ<N we use general-purpose code.
|
|
*/
|
|
rep->iterationscount = 0;
|
|
if( nz==n )
|
|
{
|
|
|
|
/*
|
|
* NZ=N, degenerate problem.
|
|
* No need to run optimizer.
|
|
*/
|
|
v = 0.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = v+y->ptr.p_double[i];
|
|
}
|
|
v = v/n;
|
|
if( ae_isfinite(cnstrleft, _state) )
|
|
{
|
|
*a = cnstrleft;
|
|
}
|
|
else
|
|
{
|
|
*a = v;
|
|
}
|
|
*b = (double)(1);
|
|
*c = (double)(1);
|
|
if( ae_isfinite(cnstrright, _state) )
|
|
{
|
|
*d = cnstrright;
|
|
}
|
|
else
|
|
{
|
|
*d = *a;
|
|
}
|
|
*g = (double)(1);
|
|
lsfit_logisticfit45errors(x, y, n, *a, *b, *c, *d, *g, rep, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Non-degenerate problem.
|
|
* Determine scale of data.
|
|
*/
|
|
scalex = x->ptr.p_double[nz+(n-nz)/2];
|
|
ae_assert(ae_fp_greater(scalex,(double)(0)), "LogisticFitX: internal error", _state);
|
|
v = 0.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
v = v+y->ptr.p_double[i];
|
|
}
|
|
v = v/n;
|
|
scaley = 0.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
scaley = scaley+ae_sqr(y->ptr.p_double[i]-v, _state);
|
|
}
|
|
scaley = ae_sqrt(scaley/n, _state);
|
|
if( ae_fp_eq(scaley,(double)(0)) )
|
|
{
|
|
scaley = 1.0;
|
|
}
|
|
ae_vector_set_length(&s, 5, _state);
|
|
s.ptr.p_double[0] = scaley;
|
|
s.ptr.p_double[1] = 0.1;
|
|
s.ptr.p_double[2] = scalex;
|
|
s.ptr.p_double[3] = scaley;
|
|
s.ptr.p_double[4] = 0.1;
|
|
ae_vector_set_length(&p0, 5, _state);
|
|
p0.ptr.p_double[0] = (double)(0);
|
|
p0.ptr.p_double[1] = (double)(0);
|
|
p0.ptr.p_double[2] = (double)(0);
|
|
p0.ptr.p_double[3] = (double)(0);
|
|
p0.ptr.p_double[4] = (double)(0);
|
|
ae_vector_set_length(&bndl, 5, _state);
|
|
ae_vector_set_length(&bndu, 5, _state);
|
|
ae_vector_set_length(&bndl1, 5, _state);
|
|
ae_vector_set_length(&bndu1, 5, _state);
|
|
ae_vector_set_length(&bndl2, 5, _state);
|
|
ae_vector_set_length(&bndu2, 5, _state);
|
|
minlmcreatevj(5, n+5, &p0, &state, _state);
|
|
minlmsetscale(&state, &s, _state);
|
|
minlmsetcond(&state, epsx, maxits, _state);
|
|
minlmsetxrep(&state, ae_true, _state);
|
|
ae_vector_set_length(&p1, 5, _state);
|
|
ae_vector_set_length(&p2, 5, _state);
|
|
|
|
/*
|
|
* Is it 4PL problem?
|
|
*/
|
|
if( is4pl )
|
|
{
|
|
|
|
/*
|
|
* Run outer iterations
|
|
*/
|
|
*a = (double)(0);
|
|
*b = (double)(1);
|
|
*c = (double)(1);
|
|
*d = (double)(1);
|
|
*g = (double)(1);
|
|
fbest = ae_maxrealnumber;
|
|
for(outerit=0; outerit<=rscnt-1; outerit++)
|
|
{
|
|
|
|
/*
|
|
* Prepare initial point; use B>0
|
|
*/
|
|
if( ae_isfinite(cnstrleft, _state) )
|
|
{
|
|
p1.ptr.p_double[0] = cnstrleft;
|
|
}
|
|
else
|
|
{
|
|
p1.ptr.p_double[0] = y->ptr.p_double[0]+0.15*scaley*(hqrnduniformr(&rs, _state)-0.5);
|
|
}
|
|
p1.ptr.p_double[1] = 0.5+hqrnduniformr(&rs, _state);
|
|
p1.ptr.p_double[2] = x->ptr.p_double[nz+hqrnduniformi(&rs, n-nz, _state)];
|
|
if( ae_isfinite(cnstrright, _state) )
|
|
{
|
|
p1.ptr.p_double[3] = cnstrright;
|
|
}
|
|
else
|
|
{
|
|
p1.ptr.p_double[3] = y->ptr.p_double[n-1]+0.25*scaley*(hqrnduniformr(&rs, _state)-0.5);
|
|
}
|
|
p1.ptr.p_double[4] = 1.0;
|
|
|
|
/*
|
|
* Run optimization with tight constraints and increased regularization
|
|
*/
|
|
if( ae_isfinite(cnstrleft, _state) )
|
|
{
|
|
bndl.ptr.p_double[0] = cnstrleft;
|
|
bndu.ptr.p_double[0] = cnstrleft;
|
|
}
|
|
else
|
|
{
|
|
bndl.ptr.p_double[0] = _state->v_neginf;
|
|
bndu.ptr.p_double[0] = _state->v_posinf;
|
|
}
|
|
bndl.ptr.p_double[1] = 0.5;
|
|
bndu.ptr.p_double[1] = 2.0;
|
|
bndl.ptr.p_double[2] = 0.5*scalex;
|
|
bndu.ptr.p_double[2] = 2.0*scalex;
|
|
if( ae_isfinite(cnstrright, _state) )
|
|
{
|
|
bndl.ptr.p_double[3] = cnstrright;
|
|
bndu.ptr.p_double[3] = cnstrright;
|
|
}
|
|
else
|
|
{
|
|
bndl.ptr.p_double[3] = _state->v_neginf;
|
|
bndu.ptr.p_double[3] = _state->v_posinf;
|
|
}
|
|
bndl.ptr.p_double[4] = 1.0;
|
|
bndu.ptr.p_double[4] = 1.0;
|
|
minlmsetbc(&state, &bndl, &bndu, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, 100*lambdav, &state, &replm, &p1, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Relax constraints, run optimization one more time
|
|
*/
|
|
bndl.ptr.p_double[1] = 0.1;
|
|
bndu.ptr.p_double[1] = 10.0;
|
|
bndl.ptr.p_double[2] = ae_machineepsilon*scalex;
|
|
bndu.ptr.p_double[2] = scalex/ae_machineepsilon;
|
|
minlmsetbc(&state, &bndl, &bndu, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p1, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Relax constraints more, run optimization one more time
|
|
*/
|
|
bndl.ptr.p_double[1] = 0.01;
|
|
bndu.ptr.p_double[1] = 100.0;
|
|
minlmsetbc(&state, &bndl, &bndu, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p1, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Relax constraints ever more, run optimization one more time
|
|
*/
|
|
bndl.ptr.p_double[1] = 0.001;
|
|
bndu.ptr.p_double[1] = 1000.0;
|
|
minlmsetbc(&state, &bndl, &bndu, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p1, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Compare results with best value found so far.
|
|
*/
|
|
if( ae_fp_less(flast,fbest) )
|
|
{
|
|
*a = p1.ptr.p_double[0];
|
|
*b = p1.ptr.p_double[1];
|
|
*c = p1.ptr.p_double[2];
|
|
*d = p1.ptr.p_double[3];
|
|
*g = p1.ptr.p_double[4];
|
|
fbest = flast;
|
|
}
|
|
}
|
|
lsfit_logisticfit45errors(x, y, n, *a, *b, *c, *d, *g, rep, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Well.... we have 5PL fit, and we have to test two separate branches:
|
|
* B>0 and B<0, because of asymmetry in the curve. First, we run optimization
|
|
* with tight constraints two times, in order to determine better sign for B.
|
|
*
|
|
* Run outer iterations
|
|
*/
|
|
*a = (double)(0);
|
|
*b = (double)(1);
|
|
*c = (double)(1);
|
|
*d = (double)(1);
|
|
*g = (double)(1);
|
|
fbest = ae_maxrealnumber;
|
|
for(outerit=0; outerit<=rscnt-1; outerit++)
|
|
{
|
|
|
|
/*
|
|
* First, we try positive B.
|
|
*/
|
|
p1.ptr.p_double[0] = y->ptr.p_double[0]+0.15*scaley*(hqrnduniformr(&rs, _state)-0.5);
|
|
p1.ptr.p_double[1] = 0.5+hqrnduniformr(&rs, _state);
|
|
p1.ptr.p_double[2] = x->ptr.p_double[nz+hqrnduniformi(&rs, n-nz, _state)];
|
|
p1.ptr.p_double[3] = y->ptr.p_double[n-1]+0.25*scaley*(hqrnduniformr(&rs, _state)-0.5);
|
|
p1.ptr.p_double[4] = 1.0;
|
|
bndl1.ptr.p_double[0] = _state->v_neginf;
|
|
bndu1.ptr.p_double[0] = _state->v_posinf;
|
|
bndl1.ptr.p_double[1] = 0.5;
|
|
bndu1.ptr.p_double[1] = 2.0;
|
|
bndl1.ptr.p_double[2] = 0.5*scalex;
|
|
bndu1.ptr.p_double[2] = 2.0*scalex;
|
|
bndl1.ptr.p_double[3] = _state->v_neginf;
|
|
bndu1.ptr.p_double[3] = _state->v_posinf;
|
|
bndl1.ptr.p_double[4] = 0.5;
|
|
bndu1.ptr.p_double[4] = 2.0;
|
|
if( ae_isfinite(cnstrleft, _state) )
|
|
{
|
|
p1.ptr.p_double[0] = cnstrleft;
|
|
bndl1.ptr.p_double[0] = cnstrleft;
|
|
bndu1.ptr.p_double[0] = cnstrleft;
|
|
}
|
|
if( ae_isfinite(cnstrright, _state) )
|
|
{
|
|
p1.ptr.p_double[3] = cnstrright;
|
|
bndl1.ptr.p_double[3] = cnstrright;
|
|
bndu1.ptr.p_double[3] = cnstrright;
|
|
}
|
|
minlmsetbc(&state, &bndl1, &bndu1, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, 100*lambdav, &state, &replm, &p1, &fposb, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Second attempt - with negative B (constraints are still tight).
|
|
*/
|
|
p2.ptr.p_double[0] = y->ptr.p_double[n-1]+0.15*scaley*(hqrnduniformr(&rs, _state)-0.5);
|
|
p2.ptr.p_double[1] = -(0.5+hqrnduniformr(&rs, _state));
|
|
p2.ptr.p_double[2] = x->ptr.p_double[nz+hqrnduniformi(&rs, n-nz, _state)];
|
|
p2.ptr.p_double[3] = y->ptr.p_double[0]+0.25*scaley*(hqrnduniformr(&rs, _state)-0.5);
|
|
p2.ptr.p_double[4] = 1.0;
|
|
bndl2.ptr.p_double[0] = _state->v_neginf;
|
|
bndu2.ptr.p_double[0] = _state->v_posinf;
|
|
bndl2.ptr.p_double[1] = -2.0;
|
|
bndu2.ptr.p_double[1] = -0.5;
|
|
bndl2.ptr.p_double[2] = 0.5*scalex;
|
|
bndu2.ptr.p_double[2] = 2.0*scalex;
|
|
bndl2.ptr.p_double[3] = _state->v_neginf;
|
|
bndu2.ptr.p_double[3] = _state->v_posinf;
|
|
bndl2.ptr.p_double[4] = 0.5;
|
|
bndu2.ptr.p_double[4] = 2.0;
|
|
if( ae_isfinite(cnstrleft, _state) )
|
|
{
|
|
p2.ptr.p_double[3] = cnstrleft;
|
|
bndl2.ptr.p_double[3] = cnstrleft;
|
|
bndu2.ptr.p_double[3] = cnstrleft;
|
|
}
|
|
if( ae_isfinite(cnstrright, _state) )
|
|
{
|
|
p2.ptr.p_double[0] = cnstrright;
|
|
bndl2.ptr.p_double[0] = cnstrright;
|
|
bndu2.ptr.p_double[0] = cnstrright;
|
|
}
|
|
minlmsetbc(&state, &bndl2, &bndu2, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, 100*lambdav, &state, &replm, &p2, &fnegb, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Select best version of B sign
|
|
*/
|
|
if( ae_fp_less(fposb,fnegb) )
|
|
{
|
|
|
|
/*
|
|
* Prepare relaxed constraints assuming that B is positive
|
|
*/
|
|
bndl1.ptr.p_double[1] = 0.1;
|
|
bndu1.ptr.p_double[1] = 10.0;
|
|
bndl1.ptr.p_double[2] = ae_machineepsilon*scalex;
|
|
bndu1.ptr.p_double[2] = scalex/ae_machineepsilon;
|
|
bndl1.ptr.p_double[4] = 0.1;
|
|
bndu1.ptr.p_double[4] = 10.0;
|
|
minlmsetbc(&state, &bndl1, &bndu1, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p1, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Prepare stronger relaxation of constraints
|
|
*/
|
|
bndl1.ptr.p_double[1] = 0.01;
|
|
bndu1.ptr.p_double[1] = 100.0;
|
|
minlmsetbc(&state, &bndl1, &bndu1, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p1, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Prepare stronger relaxation of constraints
|
|
*/
|
|
bndl1.ptr.p_double[1] = 0.001;
|
|
bndu1.ptr.p_double[1] = 1000.0;
|
|
minlmsetbc(&state, &bndl1, &bndu1, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p1, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Compare results with best value found so far.
|
|
*/
|
|
if( ae_fp_less(flast,fbest) )
|
|
{
|
|
*a = p1.ptr.p_double[0];
|
|
*b = p1.ptr.p_double[1];
|
|
*c = p1.ptr.p_double[2];
|
|
*d = p1.ptr.p_double[3];
|
|
*g = p1.ptr.p_double[4];
|
|
fbest = flast;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Prepare relaxed constraints assuming that B is negative
|
|
*/
|
|
bndl2.ptr.p_double[1] = -10.0;
|
|
bndu2.ptr.p_double[1] = -0.1;
|
|
bndl2.ptr.p_double[2] = ae_machineepsilon*scalex;
|
|
bndu2.ptr.p_double[2] = scalex/ae_machineepsilon;
|
|
bndl2.ptr.p_double[4] = 0.1;
|
|
bndu2.ptr.p_double[4] = 10.0;
|
|
minlmsetbc(&state, &bndl2, &bndu2, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p2, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Prepare stronger relaxation
|
|
*/
|
|
bndl2.ptr.p_double[1] = -100.0;
|
|
bndu2.ptr.p_double[1] = -0.01;
|
|
minlmsetbc(&state, &bndl2, &bndu2, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p2, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Prepare stronger relaxation
|
|
*/
|
|
bndl2.ptr.p_double[1] = -1000.0;
|
|
bndu2.ptr.p_double[1] = -0.001;
|
|
minlmsetbc(&state, &bndl2, &bndu2, _state);
|
|
lsfit_logisticfitinternal(x, y, n, is4pl, lambdav, &state, &replm, &p2, &flast, _state);
|
|
rep->iterationscount = rep->iterationscount+replm.iterationscount;
|
|
|
|
/*
|
|
* Compare results with best value found so far.
|
|
*/
|
|
if( ae_fp_less(flast,fbest) )
|
|
{
|
|
*a = p2.ptr.p_double[0];
|
|
*b = p2.ptr.p_double[1];
|
|
*c = p2.ptr.p_double[2];
|
|
*d = p2.ptr.p_double[3];
|
|
*g = p2.ptr.p_double[4];
|
|
fbest = flast;
|
|
}
|
|
}
|
|
}
|
|
lsfit_logisticfit45errors(x, y, n, *a, *b, *c, *d, *g, rep, _state);
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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);
|
|
memset(&locb, 0, sizeof(locb));
|
|
memset(&locrep, 0, sizeof(locrep));
|
|
*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 = (double)(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).
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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);
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&xc, 0, sizeof(xc));
|
|
memset(&yc, 0, sizeof(yc));
|
|
memset(&dc, 0, sizeof(dc));
|
|
*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] = (double)(1);
|
|
}
|
|
barycentricfitfloaterhormannwc(x, y, &w, n, &xc, &yc, &dc, 0, m, info, b, rep, _state);
|
|
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
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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)
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
-- 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);
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&xc, 0, sizeof(xc));
|
|
memset(&yc, 0, sizeof(yc));
|
|
memset(&dc, 0, sizeof(dc));
|
|
*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] = (double)(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).
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
-- 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);
|
|
memset(&w, 0, sizeof(w));
|
|
memset(&xc, 0, sizeof(xc));
|
|
memset(&yc, 0, sizeof(yc));
|
|
memset(&dc, 0, sizeof(dc));
|
|
*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] = (double)(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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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);
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_cmatrix, 0, sizeof(_cmatrix));
|
|
memset(&tau, 0, sizeof(tau));
|
|
memset(&q, 0, sizeof(q));
|
|
memset(&f2, 0, sizeof(f2));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memset(&c0, 0, sizeof(c0));
|
|
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 = (double)(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] = (double)(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);
|
|
rmatrixgemm(n, m-k, m, 1.0, fmatrix, 0, 0, 0, &q, k, 0, 1, 0.0, &f2, 0, 0, _state);
|
|
lsfit_lsfitlinearinternal(y, w, &f2, n, m-k, info, &tmp, rep, _state);
|
|
rep->taskrcond = (double)(-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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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);
|
|
memset(&w, 0, sizeof(w));
|
|
*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] = (double)(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.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
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);
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&w, 0, sizeof(w));
|
|
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] = (double)(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,(double)(0)), "LSFitCreateWF: DiffStep<=0!", _state);
|
|
state->teststep = (double)(0);
|
|
state->diffstep = diffstep;
|
|
state->npoints = n;
|
|
state->nweights = n;
|
|
state->wkind = 1;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 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->c0, k, _state);
|
|
ae_vector_set_length(&state->c1, k, _state);
|
|
ae_v_move(&state->c0.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->c1.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
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;
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
minlmcreatev(k, n, &state->c0, 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,(double)(0)), "LSFitCreateF: DiffStep<=0!", _state);
|
|
state->teststep = (double)(0);
|
|
state->diffstep = diffstep;
|
|
state->npoints = n;
|
|
state->wkind = 0;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 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->c0, k, _state);
|
|
ae_vector_set_length(&state->c1, k, _state);
|
|
ae_v_move(&state->c0.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->c1.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
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;
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
minlmcreatev(k, n, &state->c0, 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 = (double)(0);
|
|
state->diffstep = (double)(0);
|
|
state->npoints = n;
|
|
state->nweights = n;
|
|
state->wkind = 1;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 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->c0, k, _state);
|
|
ae_vector_set_length(&state->c1, k, _state);
|
|
ae_v_move(&state->c0.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->c1.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_vector_set_length(&state->g, k, _state);
|
|
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;
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
if( cheapfg )
|
|
{
|
|
minlmcreatevgj(k, n, &state->c0, &state->optstate, _state);
|
|
}
|
|
else
|
|
{
|
|
minlmcreatevj(k, n, &state->c0, &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 = (double)(0);
|
|
state->diffstep = (double)(0);
|
|
state->npoints = n;
|
|
state->wkind = 0;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 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->c0, k, _state);
|
|
ae_vector_set_length(&state->c1, k, _state);
|
|
ae_v_move(&state->c0.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->c1.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_vector_set_length(&state->x, m, _state);
|
|
ae_vector_set_length(&state->g, k, _state);
|
|
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;
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
if( cheapfg )
|
|
{
|
|
minlmcreatevgj(k, n, &state->c0, &state->optstate, _state);
|
|
}
|
|
else
|
|
{
|
|
minlmcreatevj(k, n, &state->c0, &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 = (double)(0);
|
|
state->diffstep = (double)(0);
|
|
state->npoints = n;
|
|
state->nweights = n;
|
|
state->wkind = 1;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 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->c0, k, _state);
|
|
ae_vector_set_length(&state->c1, k, _state);
|
|
ae_v_move(&state->c0.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->c1.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
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->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;
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
minlmcreatefgh(k, &state->c0, &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 = (double)(0);
|
|
state->diffstep = (double)(0);
|
|
state->npoints = n;
|
|
state->wkind = 0;
|
|
state->m = m;
|
|
state->k = k;
|
|
lsfitsetcond(state, 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->c0, k, _state);
|
|
ae_vector_set_length(&state->c1, k, _state);
|
|
ae_v_move(&state->c0.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
ae_v_move(&state->c1.ptr.p_double[0], 1, &c->ptr.p_double[0], 1, ae_v_len(0,k-1));
|
|
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);
|
|
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;
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
minlmcreatefgh(k, &state->c0, &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
|
|
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 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 epsx,
|
|
ae_int_t maxits,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(epsx, _state), "LSFitSetCond: EpsX is not finite!", _state);
|
|
ae_assert(ae_fp_greater_eq(epsx,(double)(0)), "LSFitSetCond: negative EpsX!", _state);
|
|
ae_assert(maxits>=0, "LSFitSetCond: negative MaxIts!", _state);
|
|
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,(double)(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],(double)(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];
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets linear constraints for underlying optimizer
|
|
|
|
Linear constraints are inactive by default (after initial creation).
|
|
They are preserved until explicitly turned off with another SetLC() call.
|
|
|
|
INPUT PARAMETERS:
|
|
State - structure stores algorithm state
|
|
C - linear constraints, array[K,N+1].
|
|
Each row of C represents one constraint, either equality
|
|
or inequality (see below):
|
|
* first N elements correspond to coefficients,
|
|
* last element corresponds to the right part.
|
|
All elements of C (including right part) must be finite.
|
|
CT - type of constraints, array[K]:
|
|
* if CT[i]>0, then I-th constraint is C[i,*]*x >= C[i,n+1]
|
|
* if CT[i]=0, then I-th constraint is C[i,*]*x = C[i,n+1]
|
|
* if CT[i]<0, then I-th constraint is C[i,*]*x <= C[i,n+1]
|
|
K - number of equality/inequality constraints, K>=0:
|
|
* if given, only leading K elements of C/CT are used
|
|
* if not given, automatically determined from sizes of C/CT
|
|
|
|
IMPORTANT: if you have linear constraints, it is strongly recommended to
|
|
set scale of variables with lsfitsetscale(). QP solver which is
|
|
used to calculate linearly constrained steps heavily relies on
|
|
good scaling of input problems.
|
|
|
|
NOTE: linear (non-box) constraints are satisfied only approximately -
|
|
there always exists some violation due to numerical errors and
|
|
algorithmic limitations.
|
|
|
|
NOTE: general linear constraints add significant overhead to solution
|
|
process. Although solver performs roughly same amount of iterations
|
|
(when compared with similar box-only constrained problem), each
|
|
iteration now involves solution of linearly constrained QP
|
|
subproblem, which requires ~3-5 times more Cholesky decompositions.
|
|
Thus, if you can reformulate your problem in such way this it has
|
|
only box constraints, it may be beneficial to do so.
|
|
|
|
-- ALGLIB --
|
|
Copyright 29.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void lsfitsetlc(lsfitstate* state,
|
|
/* Real */ ae_matrix* c,
|
|
/* Integer */ ae_vector* ct,
|
|
ae_int_t k,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t n;
|
|
|
|
|
|
n = state->k;
|
|
|
|
/*
|
|
* First, check for errors in the inputs
|
|
*/
|
|
ae_assert(k>=0, "LSFitSetLC: K<0", _state);
|
|
ae_assert(c->cols>=n+1||k==0, "LSFitSetLC: Cols(C)<N+1", _state);
|
|
ae_assert(c->rows>=k, "LSFitSetLC: Rows(C)<K", _state);
|
|
ae_assert(ct->cnt>=k, "LSFitSetLC: Length(CT)<K", _state);
|
|
ae_assert(apservisfinitematrix(c, k, n+1, _state), "LSFitSetLC: C contains infinite or NaN values!", _state);
|
|
|
|
/*
|
|
* Handle zero K
|
|
*/
|
|
if( k==0 )
|
|
{
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Equality constraints are stored first, in the upper
|
|
* NEC rows of State.CLEIC matrix. Inequality constraints
|
|
* are stored in the next NIC rows.
|
|
*
|
|
* NOTE: we convert inequality constraints to the form
|
|
* A*x<=b before copying them.
|
|
*/
|
|
rmatrixsetlengthatleast(&state->cleic, k, n+1, _state);
|
|
state->nec = 0;
|
|
state->nic = 0;
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
if( ct->ptr.p_int[i]==0 )
|
|
{
|
|
ae_v_move(&state->cleic.ptr.pp_double[state->nec][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
|
|
state->nec = state->nec+1;
|
|
}
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
if( ct->ptr.p_int[i]!=0 )
|
|
{
|
|
if( ct->ptr.p_int[i]>0 )
|
|
{
|
|
ae_v_moveneg(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
|
|
}
|
|
else
|
|
{
|
|
ae_v_move(&state->cleic.ptr.pp_double[state->nec+state->nic][0], 1, &c->ptr.pp_double[i][0], 1, ae_v_len(0,n));
|
|
}
|
|
state->nic = state->nic+1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 = 359;
|
|
m = -58;
|
|
k = -919;
|
|
i = -909;
|
|
j = 81;
|
|
j1 = 255;
|
|
info = 74;
|
|
lx = -788;
|
|
lf = 809;
|
|
ld = 205;
|
|
rx = -838;
|
|
rf = 939;
|
|
rd = -526;
|
|
v = 763;
|
|
vv = -541;
|
|
relcnt = -698;
|
|
}
|
|
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;
|
|
ivectorsetlengthatleast(&state->tmpct, state->nec+state->nic, _state);
|
|
for(i=0; i<=state->nec-1; i++)
|
|
{
|
|
state->tmpct.ptr.p_int[i] = 0;
|
|
}
|
|
for(i=0; i<=state->nic-1; i++)
|
|
{
|
|
state->tmpct.ptr.p_int[state->nec+i] = -1;
|
|
}
|
|
minlmsetcond(&state->optstate, 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);
|
|
minlmsetlc(&state->optstate, &state->cleic, &state->tmpct, state->nec+state->nic, _state);
|
|
|
|
/*
|
|
* Check that user-supplied gradient is correct
|
|
*/
|
|
lsfit_lsfitclearrequestfields(state, _state);
|
|
if( !(ae_fp_greater(state->teststep,(double)(0))&&state->optalgo==1) )
|
|
{
|
|
goto lbl_14;
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->c.ptr.p_double[i] = state->c0.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);
|
|
}
|
|
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 = (double)(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 = (double)(0);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->optstate.g.ptr.p_double[i] = (double)(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 = (double)(0);
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
state->optstate.g.ptr.p_double[i] = (double)(0);
|
|
}
|
|
for(i=0; i<=k-1; i++)
|
|
{
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
state->optstate.h.ptr.pp_double[i][j] = (double)(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:
|
|
|
|
/*
|
|
* Extract results
|
|
*
|
|
* NOTE: reverse communication protocol used by this unit does NOT
|
|
* allow us to reallocate State.C[] array. Thus, we extract
|
|
* results to the temporary variable in order to avoid possible
|
|
* reallocation.
|
|
*/
|
|
minlmresults(&state->optstate, &state->c1, &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 = (double)(0);
|
|
state->repwrmserror = (double)(0);
|
|
state->repavgerror = (double)(0);
|
|
state->repavgrelerror = (double)(0);
|
|
state->repmaxerror = (double)(0);
|
|
relcnt = (double)(0);
|
|
i = 0;
|
|
lbl_53:
|
|
if( i>n-1 )
|
|
{
|
|
goto lbl_55;
|
|
}
|
|
ae_v_move(&state->c.ptr.p_double[0], 1, &state->c1.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],(double)(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,(double)(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,(double)(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] = (double)(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:
|
|
* -8 optimizer detected NAN/INF in the target
|
|
function and/or gradient
|
|
* -7 gradient verification failed.
|
|
See LSFitSetGradientCheck() for more information.
|
|
* -3 inconsistent constraints
|
|
* 2 relative step is no more than EpsX.
|
|
* 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->c1.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,(double)(0)), "LSFitSetGradientCheck: invalid argument TestStep(TestStep<0)", _state);
|
|
state->teststep = teststep;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function analyzes section of curve for processing by RDP algorithm:
|
|
given set of points X,Y with indexes [I0,I1] it returns point with
|
|
worst deviation from linear model (non-parametric version which sees curve
|
|
as Y(x)).
|
|
|
|
Input parameters:
|
|
X, Y - SORTED arrays.
|
|
I0,I1 - interval (boundaries included) to process
|
|
Eps - desired precision
|
|
|
|
OUTPUT PARAMETERS:
|
|
WorstIdx - index of worst point
|
|
WorstError - error at worst point
|
|
|
|
NOTE: this function guarantees that it returns exactly zero for a section
|
|
with less than 3 points.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void lsfit_rdpanalyzesection(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t i0,
|
|
ae_int_t i1,
|
|
ae_int_t* worstidx,
|
|
double* worsterror,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
double xleft;
|
|
double xright;
|
|
double vx;
|
|
double ve;
|
|
double a;
|
|
double b;
|
|
|
|
*worstidx = 0;
|
|
*worsterror = 0;
|
|
|
|
xleft = x->ptr.p_double[i0];
|
|
xright = x->ptr.p_double[i1];
|
|
if( i1-i0+1<3||ae_fp_eq(xright,xleft) )
|
|
{
|
|
*worstidx = i0;
|
|
*worsterror = 0.0;
|
|
return;
|
|
}
|
|
a = (y->ptr.p_double[i1]-y->ptr.p_double[i0])/(xright-xleft);
|
|
b = (y->ptr.p_double[i0]*xright-y->ptr.p_double[i1]*xleft)/(xright-xleft);
|
|
*worstidx = -1;
|
|
*worsterror = (double)(0);
|
|
for(i=i0+1; i<=i1-1; i++)
|
|
{
|
|
vx = x->ptr.p_double[i];
|
|
ve = ae_fabs(a*vx+b-y->ptr.p_double[i], _state);
|
|
if( (ae_fp_greater(vx,xleft)&&ae_fp_less(vx,xright))&&ae_fp_greater(ve,*worsterror) )
|
|
{
|
|
*worsterror = ve;
|
|
*worstidx = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Recursive splitting of interval [I0,I1] (right boundary included) with RDP
|
|
algorithm (non-parametric version which sees curve as Y(x)).
|
|
|
|
Input parameters:
|
|
X, Y - SORTED arrays.
|
|
I0,I1 - interval (boundaries included) to process
|
|
Eps - desired precision
|
|
XOut,YOut - preallocated output arrays large enough to store result;
|
|
XOut[0..1], YOut[0..1] contain first and last points of
|
|
curve
|
|
NOut - must contain 2 on input
|
|
|
|
OUTPUT PARAMETERS:
|
|
XOut, YOut - curve generated by RDP algorithm, UNSORTED
|
|
NOut - number of points in curve
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 02.10.2014 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void lsfit_rdprecursive(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t i0,
|
|
ae_int_t i1,
|
|
double eps,
|
|
/* Real */ ae_vector* xout,
|
|
/* Real */ ae_vector* yout,
|
|
ae_int_t* nout,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t worstidx;
|
|
double worsterror;
|
|
|
|
|
|
ae_assert(ae_fp_greater(eps,(double)(0)), "RDPRecursive: internal error, Eps<0", _state);
|
|
lsfit_rdpanalyzesection(x, y, i0, i1, &worstidx, &worsterror, _state);
|
|
if( ae_fp_less_eq(worsterror,eps) )
|
|
{
|
|
return;
|
|
}
|
|
xout->ptr.p_double[*nout] = x->ptr.p_double[worstidx];
|
|
yout->ptr.p_double[*nout] = y->ptr.p_double[worstidx];
|
|
*nout = *nout+1;
|
|
if( worstidx-i0<i1-worstidx )
|
|
{
|
|
lsfit_rdprecursive(x, y, i0, worstidx, eps, xout, yout, nout, _state);
|
|
lsfit_rdprecursive(x, y, worstidx, i1, eps, xout, yout, nout, _state);
|
|
}
|
|
else
|
|
{
|
|
lsfit_rdprecursive(x, y, worstidx, i1, eps, xout, yout, nout, _state);
|
|
lsfit_rdprecursive(x, y, i0, worstidx, eps, xout, yout, nout, _state);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Internal 4PL/5PL fitting function.
|
|
|
|
Accepts X, Y and already initialized and prepared MinLMState structure.
|
|
On input P1 contains initial guess, on output it contains solution. FLast
|
|
stores function value at P1.
|
|
*************************************************************************/
|
|
static void lsfit_logisticfitinternal(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
ae_bool is4pl,
|
|
double lambdav,
|
|
minlmstate* state,
|
|
minlmreport* replm,
|
|
/* Real */ ae_vector* p1,
|
|
double* flast,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double ta;
|
|
double tb;
|
|
double tc;
|
|
double td;
|
|
double tg;
|
|
double vp0;
|
|
double vp1;
|
|
|
|
*flast = 0;
|
|
|
|
minlmrestartfrom(state, p1, _state);
|
|
while(minlmiteration(state, _state))
|
|
{
|
|
ta = state->x.ptr.p_double[0];
|
|
tb = state->x.ptr.p_double[1];
|
|
tc = state->x.ptr.p_double[2];
|
|
td = state->x.ptr.p_double[3];
|
|
tg = state->x.ptr.p_double[4];
|
|
if( state->xupdated )
|
|
{
|
|
|
|
/*
|
|
* Save best function value obtained so far.
|
|
*/
|
|
*flast = state->f;
|
|
continue;
|
|
}
|
|
if( state->needfi||state->needfij )
|
|
{
|
|
|
|
/*
|
|
* Function vector and Jacobian
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
ae_assert(ae_fp_greater_eq(x->ptr.p_double[i],(double)(0)), "LogisticFitInternal: integrity error", _state);
|
|
|
|
/*
|
|
* Handle zero X
|
|
*/
|
|
if( ae_fp_eq(x->ptr.p_double[i],(double)(0)) )
|
|
{
|
|
if( ae_fp_greater_eq(tb,(double)(0)) )
|
|
{
|
|
|
|
/*
|
|
* Positive or zero TB, limit X^TB subject to X->+0 is equal to zero.
|
|
*/
|
|
state->fi.ptr.p_double[i] = ta-y->ptr.p_double[i];
|
|
if( state->needfij )
|
|
{
|
|
state->j.ptr.pp_double[i][0] = (double)(1);
|
|
state->j.ptr.pp_double[i][1] = (double)(0);
|
|
state->j.ptr.pp_double[i][2] = (double)(0);
|
|
state->j.ptr.pp_double[i][3] = (double)(0);
|
|
state->j.ptr.pp_double[i][4] = (double)(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Negative TB, limit X^TB subject to X->+0 is equal to +INF.
|
|
*/
|
|
state->fi.ptr.p_double[i] = td-y->ptr.p_double[i];
|
|
if( state->needfij )
|
|
{
|
|
state->j.ptr.pp_double[i][0] = (double)(0);
|
|
state->j.ptr.pp_double[i][1] = (double)(0);
|
|
state->j.ptr.pp_double[i][2] = (double)(0);
|
|
state->j.ptr.pp_double[i][3] = (double)(1);
|
|
state->j.ptr.pp_double[i][4] = (double)(0);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Positive X.
|
|
* Prepare VP0/VP1, it may become infinite or nearly overflow in some rare cases,
|
|
* handle these cases
|
|
*/
|
|
vp0 = ae_pow(x->ptr.p_double[i]/tc, tb, _state);
|
|
if( is4pl )
|
|
{
|
|
vp1 = 1+vp0;
|
|
}
|
|
else
|
|
{
|
|
vp1 = ae_pow(1+vp0, tg, _state);
|
|
}
|
|
if( (!ae_isfinite(vp1, _state)||ae_fp_greater(vp0,1.0E50))||ae_fp_greater(vp1,1.0E50) )
|
|
{
|
|
|
|
/*
|
|
* VP0/VP1 are not finite, assume that it is +INF or -INF
|
|
*/
|
|
state->fi.ptr.p_double[i] = td-y->ptr.p_double[i];
|
|
if( state->needfij )
|
|
{
|
|
state->j.ptr.pp_double[i][0] = (double)(0);
|
|
state->j.ptr.pp_double[i][1] = (double)(0);
|
|
state->j.ptr.pp_double[i][2] = (double)(0);
|
|
state->j.ptr.pp_double[i][3] = (double)(1);
|
|
state->j.ptr.pp_double[i][4] = (double)(0);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* VP0/VP1 are finite, normal processing
|
|
*/
|
|
if( is4pl )
|
|
{
|
|
state->fi.ptr.p_double[i] = td+(ta-td)/vp1-y->ptr.p_double[i];
|
|
if( state->needfij )
|
|
{
|
|
state->j.ptr.pp_double[i][0] = 1/vp1;
|
|
state->j.ptr.pp_double[i][1] = -(ta-td)*vp0*ae_log(x->ptr.p_double[i]/tc, _state)/ae_sqr(vp1, _state);
|
|
state->j.ptr.pp_double[i][2] = (ta-td)*(tb/tc)*vp0/ae_sqr(vp1, _state);
|
|
state->j.ptr.pp_double[i][3] = 1-1/vp1;
|
|
state->j.ptr.pp_double[i][4] = (double)(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state->fi.ptr.p_double[i] = td+(ta-td)/vp1-y->ptr.p_double[i];
|
|
if( state->needfij )
|
|
{
|
|
state->j.ptr.pp_double[i][0] = 1/vp1;
|
|
state->j.ptr.pp_double[i][1] = (ta-td)*(-tg)*ae_pow(1+vp0, -tg-1, _state)*vp0*ae_log(x->ptr.p_double[i]/tc, _state);
|
|
state->j.ptr.pp_double[i][2] = (ta-td)*(-tg)*ae_pow(1+vp0, -tg-1, _state)*vp0*(-tb/tc);
|
|
state->j.ptr.pp_double[i][3] = 1-1/vp1;
|
|
state->j.ptr.pp_double[i][4] = -(ta-td)/vp1*ae_log(1+vp0, _state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add regularizer
|
|
*/
|
|
for(i=0; i<=4; i++)
|
|
{
|
|
state->fi.ptr.p_double[n+i] = lambdav*state->x.ptr.p_double[i];
|
|
if( state->needfij )
|
|
{
|
|
for(j=0; j<=4; j++)
|
|
{
|
|
state->j.ptr.pp_double[n+i][j] = 0.0;
|
|
}
|
|
state->j.ptr.pp_double[n+i][i] = lambdav;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Done
|
|
*/
|
|
continue;
|
|
}
|
|
ae_assert(ae_false, "LogisticFitX: internal error", _state);
|
|
}
|
|
minlmresultsbuf(state, p1, replm, _state);
|
|
ae_assert(replm->terminationtype>0, "LogisticFitX: internal error", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Calculate errors for 4PL/5PL fit.
|
|
Leaves other fields of Rep unchanged, so caller should properly initialize
|
|
it with ClearRep() call.
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 28.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void lsfit_logisticfit45errors(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_int_t n,
|
|
double a,
|
|
double b,
|
|
double c,
|
|
double d,
|
|
double g,
|
|
lsfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t k;
|
|
double v;
|
|
double rss;
|
|
double tss;
|
|
double meany;
|
|
|
|
|
|
|
|
/*
|
|
* Calculate errors
|
|
*/
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->avgrelerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
k = 0;
|
|
rss = 0.0;
|
|
tss = 0.0;
|
|
meany = 0.0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
meany = meany+y->ptr.p_double[i];
|
|
}
|
|
meany = meany/n;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate residual from regression
|
|
*/
|
|
if( ae_fp_greater(x->ptr.p_double[i],(double)(0)) )
|
|
{
|
|
v = d+(a-d)/ae_pow(1.0+ae_pow(x->ptr.p_double[i]/c, b, _state), g, _state)-y->ptr.p_double[i];
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater_eq(b,(double)(0)) )
|
|
{
|
|
v = a-y->ptr.p_double[i];
|
|
}
|
|
else
|
|
{
|
|
v = d-y->ptr.p_double[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update RSS (residual sum of squares) and TSS (total sum of squares)
|
|
* which are used to calculate coefficient of determination.
|
|
*
|
|
* NOTE: we use formula R2 = 1-RSS/TSS because it has nice property of
|
|
* being equal to 0.0 if and only if model perfectly fits data.
|
|
*
|
|
* When we fit nonlinear models, there are exist multiple ways of
|
|
* determining R2, each of them giving different results. Formula
|
|
* above is the most intuitive one.
|
|
*/
|
|
rss = rss+v*v;
|
|
tss = tss+ae_sqr(y->ptr.p_double[i]-meany, _state);
|
|
|
|
/*
|
|
* Update errors
|
|
*/
|
|
rep->rmserror = rep->rmserror+ae_sqr(v, _state);
|
|
rep->avgerror = rep->avgerror+ae_fabs(v, _state);
|
|
if( ae_fp_neq(y->ptr.p_double[i],(double)(0)) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror+ae_fabs(v/y->ptr.p_double[i], _state);
|
|
k = k+1;
|
|
}
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v, _state), _state);
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/n, _state);
|
|
rep->avgerror = rep->avgerror/n;
|
|
if( k>0 )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror/k;
|
|
}
|
|
rep->r2 = 1.0-rss/tss;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_w, 0, sizeof(_w));
|
|
memset(&_xc, 0, sizeof(_xc));
|
|
memset(&_yc, 0, sizeof(_yc));
|
|
memset(&fmatrix, 0, sizeof(fmatrix));
|
|
memset(&cmatrix, 0, sizeof(cmatrix));
|
|
memset(&y2, 0, sizeof(y2));
|
|
memset(&w2, 0, sizeof(w2));
|
|
memset(&sx, 0, sizeof(sx));
|
|
memset(&sy, 0, sizeof(sy));
|
|
memset(&sd, 0, sizeof(sd));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memset(&xoriginal, 0, sizeof(xoriginal));
|
|
memset(&yoriginal, 0, sizeof(yoriginal));
|
|
memset(&lrep, 0, sizeof(lrep));
|
|
memset(&s2, 0, sizeof(s2));
|
|
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] = (double)(0);
|
|
}
|
|
bl = (double)(0);
|
|
br = (double)(0);
|
|
if( j<m-2 )
|
|
{
|
|
sy.ptr.p_double[j] = (double)(1);
|
|
}
|
|
if( j==m-2 )
|
|
{
|
|
bl = (double)(1);
|
|
}
|
|
if( j==m-1 )
|
|
{
|
|
br = (double)(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] = (double)(0);
|
|
sd.ptr.p_double[i] = (double)(0);
|
|
}
|
|
if( j%2==0 )
|
|
{
|
|
sy.ptr.p_double[j/2] = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
sd.ptr.p_double[j/2] = (double)(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] = (double)(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 = (double)(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] = (double)(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 = (double)(0);
|
|
relcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(yoriginal.ptr.p_double[i],(double)(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);
|
|
memset(&ft, 0, sizeof(ft));
|
|
memset(&q, 0, sizeof(q));
|
|
memset(&l, 0, sizeof(l));
|
|
memset(&r, 0, sizeof(r));
|
|
memset(&b, 0, sizeof(b));
|
|
memset(&wmod, 0, sizeof(wmod));
|
|
memset(&tau, 0, sizeof(tau));
|
|
memset(&nzeros, 0, sizeof(nzeros));
|
|
memset(&s, 0, sizeof(s));
|
|
memset(&sv, 0, sizeof(sv));
|
|
memset(&u, 0, sizeof(u));
|
|
memset(&vt, 0, sizeof(vt));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memset(&utb, 0, sizeof(utb));
|
|
memset(&sutb, 0, sizeof(sutb));
|
|
*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] = (double)(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] = (double)(0);
|
|
}
|
|
rep->taskrcond = (double)(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] = (double)(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] = (double)(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],(double)(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] = (double)(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rep->taskrcond = (double)(0);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
sutb.ptr.p_double[i] = (double)(0);
|
|
}
|
|
}
|
|
ae_vector_set_length(c, m, _state);
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
c->ptr.p_double[i] = (double)(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 = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->avgrelerror = (double)(0);
|
|
rep->maxerror = (double)(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],(double)(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] = (double)(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] = (double)(0);
|
|
}
|
|
for(i=0; i<=m-1; i++)
|
|
{
|
|
if( ae_fp_neq(s.ptr.p_double[i],(double)(0)) )
|
|
{
|
|
s.ptr.p_double[i] = ae_sqrt(1/s.ptr.p_double[i], _state);
|
|
}
|
|
else
|
|
{
|
|
s.ptr.p_double[i] = (double)(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] = (double)(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] = (double)(0);
|
|
}
|
|
y->ptr.p_double[i] = (double)(1);
|
|
return;
|
|
}
|
|
v = ae_fabs(t-v, _state);
|
|
if( ae_fp_less(v,s) )
|
|
{
|
|
s = v;
|
|
}
|
|
}
|
|
s2 = (double)(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);
|
|
memset(&_xc, 0, sizeof(_xc));
|
|
memset(&_yc, 0, sizeof(_yc));
|
|
memset(&y2, 0, sizeof(y2));
|
|
memset(&w2, 0, sizeof(w2));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memset(&tmp2, 0, sizeof(tmp2));
|
|
memset(&tmpdiff, 0, sizeof(tmpdiff));
|
|
memset(&bx, 0, sizeof(bx));
|
|
memset(&by, 0, sizeof(by));
|
|
memset(&bw, 0, sizeof(bw));
|
|
memset(&fmatrix, 0, sizeof(fmatrix));
|
|
memset(&cmatrix, 0, sizeof(cmatrix));
|
|
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] = (double)(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] = (double)(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 = (double)(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] = (double)(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] = (double)(1);
|
|
tmpdiff.ptr.p_double[j] = (double)(0);
|
|
}
|
|
else
|
|
{
|
|
if( j==1 )
|
|
{
|
|
tmp.ptr.p_double[j] = xc->ptr.p_double[i];
|
|
tmpdiff.ptr.p_double[j] = (double)(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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_w, 0, sizeof(_w));
|
|
memset(&_xc, 0, sizeof(_xc));
|
|
memset(&_yc, 0, sizeof(_yc));
|
|
memset(&fmatrix, 0, sizeof(fmatrix));
|
|
memset(&cmatrix, 0, sizeof(cmatrix));
|
|
memset(&y2, 0, sizeof(y2));
|
|
memset(&w2, 0, sizeof(w2));
|
|
memset(&sx, 0, sizeof(sx));
|
|
memset(&sy, 0, sizeof(sy));
|
|
memset(&sbf, 0, sizeof(sbf));
|
|
memset(&xoriginal, 0, sizeof(xoriginal));
|
|
memset(&yoriginal, 0, sizeof(yoriginal));
|
|
memset(&tmp, 0, sizeof(tmp));
|
|
memset(&lrep, 0, sizeof(lrep));
|
|
memset(&b2, 0, sizeof(b2));
|
|
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] = (double)(1);
|
|
}
|
|
barycentricbuildfloaterhormann(&sx, &sy, m, d, &b2, _state);
|
|
mx = (double)(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] = (double)(0);
|
|
}
|
|
}
|
|
y2.ptr.p_double[n+i] = (double)(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] = (double)(0);
|
|
}
|
|
sy.ptr.p_double[j] = (double)(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 = (double)(0);
|
|
relcnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_neq(yoriginal.ptr.p_double[i],(double)(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 = (double)(0);
|
|
rep->iterationscount = 0;
|
|
rep->varidx = -1;
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->avgrelerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rep->wrmserror = (double)(0);
|
|
rep->r2 = (double)(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);
|
|
memset(&_s, 0, sizeof(_s));
|
|
memset(&invrep, 0, sizeof(invrep));
|
|
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],(double)(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],(double)(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],(double)(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,(double)(0)) )
|
|
{
|
|
rep->r2 = ae_maxreal(1.0-rss/tss, 0.0, _state);
|
|
}
|
|
else
|
|
{
|
|
rep->r2 = 1.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rep->r2 = (double)(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],(double)(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,(double)(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 = (double)(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,(double)(0)) )
|
|
{
|
|
sz = (double)(1);
|
|
}
|
|
ss = (double)(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 = (double)(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,(double)(0)) )
|
|
{
|
|
sz = (double)(1);
|
|
}
|
|
ss = (double)(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] = (double)(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],(double)(0)) )
|
|
{
|
|
rep->noise.ptr.p_double[i] = noisec/w->ptr.p_double[i];
|
|
}
|
|
else
|
|
{
|
|
rep->noise.ptr.p_double[i] = (double)(0);
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
void _polynomialfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
polynomialfitreport *p = (polynomialfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _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;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void _barycentricfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
barycentricfitreport *p = (barycentricfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _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;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void _lsfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
lsfitreport *p = (lsfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_matrix_init(&p->covpar, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->errpar, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->errcurve, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->noise, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _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;
|
|
ae_matrix_init_copy(&dst->covpar, &src->covpar, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->errpar, &src->errpar, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->errcurve, &src->errcurve, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->noise, &src->noise, _state, make_automatic);
|
|
dst->r2 = src->r2;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void _lsfitstate_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
lsfitstate *p = (lsfitstate*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->c0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->c1, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->bndl, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->bndu, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->taskx, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tasky, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->taskw, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->cleic, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->c, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->g, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->h, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->wcur, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmpct, 0, DT_INT, _state, make_automatic);
|
|
ae_vector_init(&p->tmp, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmpf, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->tmpjac, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->tmpjacw, 0, 0, DT_REAL, _state, make_automatic);
|
|
_matinvreport_init(&p->invrep, _state, make_automatic);
|
|
_lsfitreport_init(&p->rep, _state, make_automatic);
|
|
_minlmstate_init(&p->optstate, _state, make_automatic);
|
|
_minlmreport_init(&p->optrep, _state, make_automatic);
|
|
_rcommstate_init(&p->rstate, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _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->epsx = src->epsx;
|
|
dst->maxits = src->maxits;
|
|
dst->stpmax = src->stpmax;
|
|
dst->xrep = src->xrep;
|
|
ae_vector_init_copy(&dst->c0, &src->c0, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->c1, &src->c1, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->bndl, &src->bndl, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->bndu, &src->bndu, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->taskx, &src->taskx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tasky, &src->tasky, _state, make_automatic);
|
|
dst->npoints = src->npoints;
|
|
ae_vector_init_copy(&dst->taskw, &src->taskw, _state, make_automatic);
|
|
dst->nweights = src->nweights;
|
|
dst->wkind = src->wkind;
|
|
dst->wits = src->wits;
|
|
dst->diffstep = src->diffstep;
|
|
dst->teststep = src->teststep;
|
|
ae_matrix_init_copy(&dst->cleic, &src->cleic, _state, make_automatic);
|
|
dst->nec = src->nec;
|
|
dst->nic = src->nic;
|
|
dst->xupdated = src->xupdated;
|
|
dst->needf = src->needf;
|
|
dst->needfg = src->needfg;
|
|
dst->needfgh = src->needfgh;
|
|
dst->pointindex = src->pointindex;
|
|
ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->c, &src->c, _state, make_automatic);
|
|
dst->f = src->f;
|
|
ae_vector_init_copy(&dst->g, &src->g, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->h, &src->h, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->wcur, &src->wcur, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpct, &src->tmpct, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmp, &src->tmp, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpf, &src->tmpf, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->tmpjac, &src->tmpjac, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->tmpjacw, &src->tmpjacw, _state, make_automatic);
|
|
dst->tmpnoise = src->tmpnoise;
|
|
_matinvreport_init_copy(&dst->invrep, &src->invrep, _state, make_automatic);
|
|
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;
|
|
_lsfitreport_init_copy(&dst->rep, &src->rep, _state, make_automatic);
|
|
_minlmstate_init_copy(&dst->optstate, &src->optstate, _state, make_automatic);
|
|
_minlmreport_init_copy(&dst->optrep, &src->optrep, _state, make_automatic);
|
|
dst->prevnpt = src->prevnpt;
|
|
dst->prevalgo = src->prevalgo;
|
|
_rcommstate_init_copy(&dst->rstate, &src->rstate, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _lsfitstate_clear(void* _p)
|
|
{
|
|
lsfitstate *p = (lsfitstate*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->c0);
|
|
ae_vector_clear(&p->c1);
|
|
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_matrix_clear(&p->cleic);
|
|
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->tmpct);
|
|
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->c0);
|
|
ae_vector_destroy(&p->c1);
|
|
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_matrix_destroy(&p->cleic);
|
|
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->tmpct);
|
|
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);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RBFV2) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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).
|
|
|
|
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)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv2create(ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv2model* s,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
_rbfv2model_clear(s);
|
|
|
|
ae_assert(nx>=1, "RBFCreate: NX<1", _state);
|
|
ae_assert(ny>=1, "RBFCreate: NY<1", _state);
|
|
|
|
/*
|
|
* Serializable parameters
|
|
*/
|
|
s->nx = nx;
|
|
s->ny = ny;
|
|
s->bf = 0;
|
|
s->nh = 0;
|
|
ae_matrix_set_length(&s->v, ny, nx+1, _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Non-serializable parameters
|
|
*/
|
|
s->lambdareg = rbfv2_defaultlambdareg;
|
|
s->maxits = rbfv2_defaultmaxits;
|
|
s->supportr = rbfv2_defaultsupportr;
|
|
s->basisfunction = rbfv2_defaultbf;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function creates buffer structure which can be used to perform
|
|
parallel RBF model evaluations (with one RBF model instance being
|
|
used from multiple threads, as long as different threads use different
|
|
instances of buffer).
|
|
|
|
This buffer object can be used with rbftscalcbuf() function (here "ts"
|
|
stands for "thread-safe", "buf" is a suffix which denotes function which
|
|
reuses previously allocated output space).
|
|
|
|
How to use it:
|
|
* create RBF model structure with rbfcreate()
|
|
* load data, tune parameters
|
|
* call rbfbuildmodel()
|
|
* call rbfcreatecalcbuffer(), once per thread working with RBF model (you
|
|
should call this function only AFTER call to rbfbuildmodel(), see below
|
|
for more information)
|
|
* call rbftscalcbuf() from different threads, with each thread working
|
|
with its own copy of buffer object.
|
|
|
|
INPUT PARAMETERS
|
|
S - RBF model
|
|
|
|
OUTPUT PARAMETERS
|
|
Buf - external buffer.
|
|
|
|
|
|
IMPORTANT: buffer object should be used only with RBF model object which
|
|
was used to initialize buffer. Any attempt to use buffer with
|
|
different object is dangerous - you may get memory violation
|
|
error because sizes of internal arrays do not fit to dimensions
|
|
of RBF structure.
|
|
|
|
IMPORTANT: you should call this function only for model which was built
|
|
with rbfbuildmodel() function, after successful invocation of
|
|
rbfbuildmodel(). Sizes of some internal structures are
|
|
determined only after model is built, so buffer object created
|
|
before model construction stage will be useless (and any
|
|
attempt to use it will result in exception).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.04.2016 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
void rbfv2createcalcbuffer(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_rbfv2calcbuffer_clear(buf);
|
|
|
|
rbfv2_allocatecalcbuffer(s, buf, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function builds hierarchical RBF model.
|
|
|
|
INPUT PARAMETERS:
|
|
X - array[N,S.NX], X-values
|
|
Y - array[N,S.NY], Y-values
|
|
ScaleVec- array[S.NX], vector of per-dimension scales
|
|
N - points count
|
|
ATerm - linear term type, 1 for linear, 2 for constant, 3 for zero.
|
|
NH - hierarchy height
|
|
RBase - base RBF radius
|
|
BF - basis function type: 0 for Gaussian, 1 for compact
|
|
LambdaNS- non-smoothness penalty coefficient. Exactly zero value means
|
|
that no penalty is applied, and even system matrix does not
|
|
contain penalty-related rows. Value of 1 means
|
|
S - RBF model, initialized by RBFCreate() call.
|
|
progress10000- variable used for progress reports, it is regularly set
|
|
to the current progress multiplied by 10000, in order to
|
|
get value in [0,10000] range. The rationale for such scaling
|
|
is that it allows us to use integer type to store progress,
|
|
which has less potential for non-atomic corruption on unprotected
|
|
reads from another threads.
|
|
You can read this variable from some other thread to get
|
|
estimate of the current progress.
|
|
Initial value of this variable is ignored, it is written by
|
|
this function, but not read.
|
|
terminationrequest - variable used for termination requests; its initial
|
|
value must be False, and you can set it to True from some
|
|
other thread. This routine regularly checks this variable
|
|
and will terminate model construction shortly upon discovering
|
|
that termination was requested.
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - updated model (for rep.terminationtype>0, unchanged otherwise)
|
|
Rep - report:
|
|
* Rep.TerminationType:
|
|
* -5 - non-distinct basis function centers were detected,
|
|
interpolation aborted
|
|
* -4 - nonconvergence of the internal SVD solver
|
|
* 1 - successful termination
|
|
* 8 terminated by user via rbfrequesttermination()
|
|
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 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv2buildhierarchical(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* scalevec,
|
|
ae_int_t aterm,
|
|
ae_int_t nh,
|
|
double rbase,
|
|
double lambdans,
|
|
rbfv2model* s,
|
|
ae_int_t* progress10000,
|
|
ae_bool* terminationrequest,
|
|
rbfv2report* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
ae_int_t bf;
|
|
ae_matrix rhs;
|
|
ae_matrix residualy;
|
|
ae_matrix v;
|
|
ae_int_t rowsperpoint;
|
|
ae_vector hidx;
|
|
ae_vector xr;
|
|
ae_vector ri;
|
|
ae_vector kdroots;
|
|
ae_vector kdnodes;
|
|
ae_vector kdsplits;
|
|
ae_vector kdboxmin;
|
|
ae_vector kdboxmax;
|
|
ae_vector cw;
|
|
ae_vector cwrange;
|
|
ae_matrix curxy;
|
|
ae_int_t curn;
|
|
ae_int_t nbasis;
|
|
kdtree curtree;
|
|
kdtree globaltree;
|
|
ae_vector x0;
|
|
ae_vector x1;
|
|
ae_vector tags;
|
|
ae_vector dist;
|
|
ae_vector nncnt;
|
|
ae_vector rowsizes;
|
|
ae_vector diagata;
|
|
ae_vector prec;
|
|
ae_vector tmpx;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t k2;
|
|
ae_int_t levelidx;
|
|
ae_int_t offsi;
|
|
ae_int_t offsj;
|
|
double val;
|
|
double criticalr;
|
|
ae_int_t cnt;
|
|
double avgdiagata;
|
|
ae_vector avgrowsize;
|
|
double sumrowsize;
|
|
double rprogress;
|
|
ae_int_t maxits;
|
|
linlsqrstate linstate;
|
|
linlsqrreport lsqrrep;
|
|
sparsematrix sparseacrs;
|
|
ae_vector densew1;
|
|
ae_vector denseb1;
|
|
rbfv2calcbuffer calcbuf;
|
|
ae_vector vr2;
|
|
ae_vector voffs;
|
|
ae_vector rowindexes;
|
|
ae_vector rowvals;
|
|
double penalty;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&rhs, 0, sizeof(rhs));
|
|
memset(&residualy, 0, sizeof(residualy));
|
|
memset(&v, 0, sizeof(v));
|
|
memset(&hidx, 0, sizeof(hidx));
|
|
memset(&xr, 0, sizeof(xr));
|
|
memset(&ri, 0, sizeof(ri));
|
|
memset(&kdroots, 0, sizeof(kdroots));
|
|
memset(&kdnodes, 0, sizeof(kdnodes));
|
|
memset(&kdsplits, 0, sizeof(kdsplits));
|
|
memset(&kdboxmin, 0, sizeof(kdboxmin));
|
|
memset(&kdboxmax, 0, sizeof(kdboxmax));
|
|
memset(&cw, 0, sizeof(cw));
|
|
memset(&cwrange, 0, sizeof(cwrange));
|
|
memset(&curxy, 0, sizeof(curxy));
|
|
memset(&curtree, 0, sizeof(curtree));
|
|
memset(&globaltree, 0, sizeof(globaltree));
|
|
memset(&x0, 0, sizeof(x0));
|
|
memset(&x1, 0, sizeof(x1));
|
|
memset(&tags, 0, sizeof(tags));
|
|
memset(&dist, 0, sizeof(dist));
|
|
memset(&nncnt, 0, sizeof(nncnt));
|
|
memset(&rowsizes, 0, sizeof(rowsizes));
|
|
memset(&diagata, 0, sizeof(diagata));
|
|
memset(&prec, 0, sizeof(prec));
|
|
memset(&tmpx, 0, sizeof(tmpx));
|
|
memset(&avgrowsize, 0, sizeof(avgrowsize));
|
|
memset(&linstate, 0, sizeof(linstate));
|
|
memset(&lsqrrep, 0, sizeof(lsqrrep));
|
|
memset(&sparseacrs, 0, sizeof(sparseacrs));
|
|
memset(&densew1, 0, sizeof(densew1));
|
|
memset(&denseb1, 0, sizeof(denseb1));
|
|
memset(&calcbuf, 0, sizeof(calcbuf));
|
|
memset(&vr2, 0, sizeof(vr2));
|
|
memset(&voffs, 0, sizeof(voffs));
|
|
memset(&rowindexes, 0, sizeof(rowindexes));
|
|
memset(&rowvals, 0, sizeof(rowvals));
|
|
_rbfv2report_clear(rep);
|
|
ae_matrix_init(&rhs, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&residualy, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&v, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&hidx, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&xr, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ri, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&kdroots, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&kdnodes, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&kdsplits, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&kdboxmin, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&kdboxmax, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&cw, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&cwrange, 0, DT_INT, _state, ae_true);
|
|
ae_matrix_init(&curxy, 0, 0, DT_REAL, _state, ae_true);
|
|
_kdtree_init(&curtree, _state, ae_true);
|
|
_kdtree_init(&globaltree, _state, ae_true);
|
|
ae_vector_init(&x0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&x1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tags, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&dist, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&nncnt, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&rowsizes, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&diagata, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&prec, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&avgrowsize, 0, DT_REAL, _state, ae_true);
|
|
_linlsqrstate_init(&linstate, _state, ae_true);
|
|
_linlsqrreport_init(&lsqrrep, _state, ae_true);
|
|
_sparsematrix_init(&sparseacrs, _state, ae_true);
|
|
ae_vector_init(&densew1, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&denseb1, 0, DT_REAL, _state, ae_true);
|
|
_rbfv2calcbuffer_init(&calcbuf, _state, ae_true);
|
|
ae_vector_init(&vr2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&voffs, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&rowindexes, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&rowvals, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(s->nx>0, "RBFV2BuildHierarchical: incorrect NX", _state);
|
|
ae_assert(s->ny>0, "RBFV2BuildHierarchical: incorrect NY", _state);
|
|
ae_assert(ae_fp_greater_eq(lambdans,(double)(0)), "RBFV2BuildHierarchical: incorrect LambdaNS", _state);
|
|
for(j=0; j<=s->nx-1; j++)
|
|
{
|
|
ae_assert(ae_fp_greater(scalevec->ptr.p_double[j],(double)(0)), "RBFV2BuildHierarchical: incorrect ScaleVec", _state);
|
|
}
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
bf = s->basisfunction;
|
|
ae_assert(bf==0||bf==1, "RBFV2BuildHierarchical: incorrect BF", _state);
|
|
|
|
/*
|
|
* Clean up communication and report fields
|
|
*/
|
|
*progress10000 = 0;
|
|
rep->maxerror = (double)(0);
|
|
rep->rmserror = (double)(0);
|
|
|
|
/*
|
|
* Quick exit when we have no points
|
|
*/
|
|
if( n==0 )
|
|
{
|
|
rbfv2_zerofill(s, nx, ny, bf, _state);
|
|
rep->terminationtype = 1;
|
|
*progress10000 = 10000;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* 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, n, ny, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
residualy.ptr.pp_double[i][j] = y->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
if( !rbfv2_rbfv2buildlinearmodel(x, &residualy, n, nx, ny, aterm, &v, _state) )
|
|
{
|
|
rbfv2_zerofill(s, nx, ny, bf, _state);
|
|
rep->terminationtype = -5;
|
|
*progress10000 = 10000;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Handle special case: multilayer model with NLayers=0.
|
|
* Quick exit.
|
|
*/
|
|
if( nh==0 )
|
|
{
|
|
rep->terminationtype = 1;
|
|
rbfv2_zerofill(s, nx, ny, bf, _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = v.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
rep->maxerror = (double)(0);
|
|
rep->rmserror = (double)(0);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(residualy.ptr.pp_double[i][j], _state), _state);
|
|
rep->rmserror = rep->rmserror+ae_sqr(residualy.ptr.pp_double[i][j], _state);
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/(n*ny), _state);
|
|
*progress10000 = 10000;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Penalty coefficient is set to LambdaNS*RBase^2.
|
|
*
|
|
* We use such normalization because VALUES of radial basis
|
|
* functions have roughly unit magnitude, but their DERIVATIVES
|
|
* are (roughly) inversely proportional to the radius. Thus,
|
|
* without additional scaling, regularization coefficient
|
|
* looses invariancy w.r.t. scaling of variables.
|
|
*/
|
|
if( ae_fp_eq(lambdans,(double)(0)) )
|
|
{
|
|
rowsperpoint = 1;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* NOTE: simplified penalty function is used, which does not provide rotation invariance
|
|
*/
|
|
rowsperpoint = 1+nx;
|
|
}
|
|
penalty = lambdans*ae_sqr(rbase, _state);
|
|
|
|
/*
|
|
* Prepare temporary structures
|
|
*/
|
|
ae_matrix_set_length(&rhs, n*rowsperpoint, ny, _state);
|
|
ae_matrix_set_length(&curxy, n, nx+ny, _state);
|
|
ae_vector_set_length(&x0, nx, _state);
|
|
ae_vector_set_length(&x1, nx, _state);
|
|
ae_vector_set_length(&tags, n, _state);
|
|
ae_vector_set_length(&dist, n, _state);
|
|
ae_vector_set_length(&vr2, n, _state);
|
|
ae_vector_set_length(&voffs, n, _state);
|
|
ae_vector_set_length(&nncnt, n, _state);
|
|
ae_vector_set_length(&rowsizes, n*rowsperpoint, _state);
|
|
ae_vector_set_length(&denseb1, n*rowsperpoint, _state);
|
|
for(i=0; i<=n*rowsperpoint-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rhs.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
curxy.ptr.pp_double[i][j] = x->ptr.pp_double[i][j]/scalevec->ptr.p_double[j];
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rhs.ptr.pp_double[i*rowsperpoint][j] = residualy.ptr.pp_double[i][j];
|
|
}
|
|
tags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(&curxy, &tags, n, nx, 0, 2, &globaltree, _state);
|
|
|
|
/*
|
|
* Generate sequence of layer radii.
|
|
* Prepare assignment of different levels to points.
|
|
*/
|
|
ae_assert(n>0, "RBFV2BuildHierarchical: integrity check failed", _state);
|
|
ae_vector_set_length(&ri, nh, _state);
|
|
for(levelidx=0; levelidx<=nh-1; levelidx++)
|
|
{
|
|
ri.ptr.p_double[levelidx] = rbase*ae_pow((double)(2), (double)(-levelidx), _state);
|
|
}
|
|
ae_vector_set_length(&hidx, n, _state);
|
|
ae_vector_set_length(&xr, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
hidx.ptr.p_int[i] = nh;
|
|
xr.ptr.p_double[i] = ae_maxrealnumber;
|
|
ae_assert(ae_fp_greater(xr.ptr.p_double[i],ri.ptr.p_double[0]), "RBFV2BuildHierarchical: integrity check failed", _state);
|
|
}
|
|
for(levelidx=0; levelidx<=nh-1; levelidx++)
|
|
{
|
|
|
|
/*
|
|
* Scan dataset points, for each such point that distance to nearest
|
|
* "support" point is larger than SupportR*Ri[LevelIdx] we:
|
|
* * set distance of current point to 0 (it is support now) and update HIdx
|
|
* * perform R-NN request with radius SupportR*Ri[LevelIdx]
|
|
* * for each point in request update its distance
|
|
*/
|
|
criticalr = s->supportr*ri.ptr.p_double[levelidx];
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( ae_fp_greater(xr.ptr.p_double[i],criticalr) )
|
|
{
|
|
|
|
/*
|
|
* Mark point as support
|
|
*/
|
|
ae_assert(hidx.ptr.p_int[i]==nh, "RBFV2BuildHierarchical: integrity check failed", _state);
|
|
hidx.ptr.p_int[i] = levelidx;
|
|
xr.ptr.p_double[i] = (double)(0);
|
|
|
|
/*
|
|
* Update neighbors
|
|
*/
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
x0.ptr.p_double[j] = x->ptr.pp_double[i][j]/scalevec->ptr.p_double[j];
|
|
}
|
|
k = kdtreequeryrnn(&globaltree, &x0, criticalr, ae_true, _state);
|
|
kdtreequeryresultstags(&globaltree, &tags, _state);
|
|
kdtreequeryresultsdistances(&globaltree, &dist, _state);
|
|
for(j=0; j<=k-1; j++)
|
|
{
|
|
xr.ptr.p_double[tags.ptr.p_int[j]] = ae_minreal(xr.ptr.p_double[tags.ptr.p_int[j]], dist.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Build multitree (with zero weights) according to hierarchy.
|
|
*
|
|
* NOTE: this code assumes that during every iteration kdNodes,
|
|
* kdSplits and CW have size which EXACTLY fits their
|
|
* contents, and that these variables are resized at each
|
|
* iteration when we add new hierarchical model.
|
|
*/
|
|
ae_vector_set_length(&kdroots, nh+1, _state);
|
|
ae_vector_set_length(&kdnodes, 0, _state);
|
|
ae_vector_set_length(&kdsplits, 0, _state);
|
|
ae_vector_set_length(&kdboxmin, nx, _state);
|
|
ae_vector_set_length(&kdboxmax, nx, _state);
|
|
ae_vector_set_length(&cw, 0, _state);
|
|
ae_vector_set_length(&cwrange, nh+1, _state);
|
|
kdtreeexplorebox(&globaltree, &kdboxmin, &kdboxmax, _state);
|
|
cwrange.ptr.p_int[0] = 0;
|
|
for(levelidx=0; levelidx<=nh-1; levelidx++)
|
|
{
|
|
|
|
/*
|
|
* Prepare radius and root offset
|
|
*/
|
|
kdroots.ptr.p_int[levelidx] = kdnodes.cnt;
|
|
|
|
/*
|
|
* Generate LevelIdx-th tree and append to multi-tree
|
|
*/
|
|
curn = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
if( hidx.ptr.p_int[i]<=levelidx )
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
curxy.ptr.pp_double[curn][j] = x->ptr.pp_double[i][j]/scalevec->ptr.p_double[j];
|
|
}
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
curxy.ptr.pp_double[curn][nx+j] = (double)(0);
|
|
}
|
|
inc(&curn, _state);
|
|
}
|
|
}
|
|
ae_assert(curn>0, "RBFV2BuildHierarchical: integrity check failed", _state);
|
|
kdtreebuild(&curxy, curn, nx, ny, 2, &curtree, _state);
|
|
rbfv2_convertandappendtree(&curtree, curn, nx, ny, &kdnodes, &kdsplits, &cw, _state);
|
|
|
|
/*
|
|
* Fill entry of CWRange (we assume that length of CW exactly fits its actual size)
|
|
*/
|
|
cwrange.ptr.p_int[levelidx+1] = cw.cnt;
|
|
}
|
|
kdroots.ptr.p_int[nh] = kdnodes.cnt;
|
|
|
|
/*
|
|
* Prepare buffer and scaled dataset
|
|
*/
|
|
rbfv2_allocatecalcbuffer(s, &calcbuf, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
curxy.ptr.pp_double[i][j] = x->ptr.pp_double[i][j]/scalevec->ptr.p_double[j];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate average row sizes for each layer; these values are used
|
|
* for smooth progress reporting (it adds some overhead, but in most
|
|
* cases - insignificant one).
|
|
*/
|
|
rvectorsetlengthatleast(&avgrowsize, nh, _state);
|
|
sumrowsize = (double)(0);
|
|
for(levelidx=0; levelidx<=nh-1; levelidx++)
|
|
{
|
|
cnt = 0;
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
x0.ptr.p_double[j] = curxy.ptr.pp_double[i][j];
|
|
}
|
|
cnt = cnt+rbfv2_designmatrixrowsize(&kdnodes, &kdsplits, &cw, &ri, &kdroots, &kdboxmin, &kdboxmax, nx, ny, nh, levelidx, rbfv2nearradius(bf, _state), &x0, &calcbuf, _state);
|
|
}
|
|
avgrowsize.ptr.p_double[levelidx] = coalesce((double)(cnt), (double)(1), _state)/coalesce((double)(n), (double)(1), _state);
|
|
sumrowsize = sumrowsize+avgrowsize.ptr.p_double[levelidx];
|
|
}
|
|
|
|
/*
|
|
* Build unconstrained model with LSQR solver, applied layer by layer
|
|
*/
|
|
for(levelidx=0; levelidx<=nh-1; levelidx++)
|
|
{
|
|
|
|
/*
|
|
* Generate A - matrix of basis functions (near radius is used)
|
|
*
|
|
* NOTE: AvgDiagATA is average value of diagonal element of A^T*A.
|
|
* It is used to calculate value of Tikhonov regularization
|
|
* coefficient.
|
|
*/
|
|
nbasis = (cwrange.ptr.p_int[levelidx+1]-cwrange.ptr.p_int[levelidx])/(nx+ny);
|
|
ae_assert(cwrange.ptr.p_int[levelidx+1]-cwrange.ptr.p_int[levelidx]==nbasis*(nx+ny), "Assertion failed", _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
x0.ptr.p_double[j] = curxy.ptr.pp_double[i][j];
|
|
}
|
|
cnt = rbfv2_designmatrixrowsize(&kdnodes, &kdsplits, &cw, &ri, &kdroots, &kdboxmin, &kdboxmax, nx, ny, nh, levelidx, rbfv2nearradius(bf, _state), &x0, &calcbuf, _state);
|
|
nncnt.ptr.p_int[i] = cnt;
|
|
for(j=0; j<=rowsperpoint-1; j++)
|
|
{
|
|
rowsizes.ptr.p_int[i*rowsperpoint+j] = cnt;
|
|
}
|
|
}
|
|
ivectorsetlengthatleast(&rowindexes, nbasis, _state);
|
|
rvectorsetlengthatleast(&rowvals, nbasis*rowsperpoint, _state);
|
|
rvectorsetlengthatleast(&diagata, nbasis, _state);
|
|
sparsecreatecrsbuf(n*rowsperpoint, nbasis, &rowsizes, &sparseacrs, _state);
|
|
avgdiagata = 0.0;
|
|
for(j=0; j<=nbasis-1; j++)
|
|
{
|
|
diagata.ptr.p_double[j] = (double)(0);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Fill design matrix row, diagonal of A^T*A
|
|
*/
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
x0.ptr.p_double[j] = curxy.ptr.pp_double[i][j];
|
|
}
|
|
rbfv2_designmatrixgeneraterow(&kdnodes, &kdsplits, &cw, &ri, &kdroots, &kdboxmin, &kdboxmax, &cwrange, nx, ny, nh, levelidx, bf, rbfv2nearradius(bf, _state), rowsperpoint, penalty, &x0, &calcbuf, &vr2, &voffs, &rowindexes, &rowvals, &cnt, _state);
|
|
ae_assert(cnt==nncnt.ptr.p_int[i], "RBFV2BuildHierarchical: integrity check failed", _state);
|
|
for(k=0; k<=rowsperpoint-1; k++)
|
|
{
|
|
for(j=0; j<=cnt-1; j++)
|
|
{
|
|
val = rowvals.ptr.p_double[j*rowsperpoint+k];
|
|
sparseset(&sparseacrs, i*rowsperpoint+k, rowindexes.ptr.p_int[j], val, _state);
|
|
avgdiagata = avgdiagata+ae_sqr(val, _state);
|
|
diagata.ptr.p_double[rowindexes.ptr.p_int[j]] = diagata.ptr.p_double[rowindexes.ptr.p_int[j]]+ae_sqr(val, _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handle possible termination requests
|
|
*/
|
|
if( *terminationrequest )
|
|
{
|
|
|
|
/*
|
|
* Request for termination was submitted, terminate immediately
|
|
*/
|
|
rbfv2_zerofill(s, nx, ny, bf, _state);
|
|
rep->terminationtype = 8;
|
|
*progress10000 = 10000;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
avgdiagata = avgdiagata/nbasis;
|
|
rvectorsetlengthatleast(&prec, nbasis, _state);
|
|
for(j=0; j<=nbasis-1; j++)
|
|
{
|
|
prec.ptr.p_double[j] = 1/coalesce(ae_sqrt(diagata.ptr.p_double[j], _state), (double)(1), _state);
|
|
}
|
|
|
|
/*
|
|
* solve
|
|
*/
|
|
maxits = coalescei(s->maxits, rbfv2_defaultmaxits, _state);
|
|
rvectorsetlengthatleast(&tmpx, nbasis, _state);
|
|
linlsqrcreate(n*rowsperpoint, nbasis, &linstate, _state);
|
|
linlsqrsetcond(&linstate, 0.0, 0.0, maxits, _state);
|
|
linlsqrsetlambdai(&linstate, ae_sqrt(s->lambdareg*avgdiagata, _state), _state);
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
for(i=0; i<=n*rowsperpoint-1; i++)
|
|
{
|
|
denseb1.ptr.p_double[i] = rhs.ptr.pp_double[i][j];
|
|
}
|
|
linlsqrsetb(&linstate, &denseb1, _state);
|
|
linlsqrrestart(&linstate, _state);
|
|
linlsqrsetxrep(&linstate, ae_true, _state);
|
|
while(linlsqriteration(&linstate, _state))
|
|
{
|
|
if( *terminationrequest )
|
|
{
|
|
|
|
/*
|
|
* Request for termination was submitted, terminate immediately
|
|
*/
|
|
rbfv2_zerofill(s, nx, ny, bf, _state);
|
|
rep->terminationtype = 8;
|
|
*progress10000 = 10000;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( linstate.needmv )
|
|
{
|
|
for(i=0; i<=nbasis-1; i++)
|
|
{
|
|
tmpx.ptr.p_double[i] = prec.ptr.p_double[i]*linstate.x.ptr.p_double[i];
|
|
}
|
|
sparsemv(&sparseacrs, &tmpx, &linstate.mv, _state);
|
|
continue;
|
|
}
|
|
if( linstate.needmtv )
|
|
{
|
|
sparsemtv(&sparseacrs, &linstate.x, &linstate.mtv, _state);
|
|
for(i=0; i<=nbasis-1; i++)
|
|
{
|
|
linstate.mtv.ptr.p_double[i] = prec.ptr.p_double[i]*linstate.mtv.ptr.p_double[i];
|
|
}
|
|
continue;
|
|
}
|
|
if( linstate.xupdated )
|
|
{
|
|
rprogress = (double)(0);
|
|
for(i=0; i<=levelidx-1; i++)
|
|
{
|
|
rprogress = rprogress+maxits*ny*avgrowsize.ptr.p_double[i];
|
|
}
|
|
rprogress = rprogress+(linlsqrpeekiterationscount(&linstate, _state)+j*maxits)*avgrowsize.ptr.p_double[levelidx];
|
|
rprogress = rprogress/(sumrowsize*maxits*ny);
|
|
rprogress = 10000*rprogress;
|
|
rprogress = ae_maxreal(rprogress, (double)(0), _state);
|
|
rprogress = ae_minreal(rprogress, (double)(10000), _state);
|
|
ae_assert(*progress10000<=ae_round(rprogress, _state)+1, "HRBF: integrity check failed (progress indicator) even after +1 safeguard correction", _state);
|
|
*progress10000 = ae_round(rprogress, _state);
|
|
continue;
|
|
}
|
|
ae_assert(ae_false, "HRBF: unexpected request from LSQR solver", _state);
|
|
}
|
|
linlsqrresults(&linstate, &densew1, &lsqrrep, _state);
|
|
ae_assert(lsqrrep.terminationtype>0, "RBFV2BuildHierarchical: integrity check failed", _state);
|
|
for(i=0; i<=nbasis-1; i++)
|
|
{
|
|
densew1.ptr.p_double[i] = prec.ptr.p_double[i]*densew1.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=nbasis-1; i++)
|
|
{
|
|
offsi = cwrange.ptr.p_int[levelidx]+(nx+ny)*i;
|
|
cw.ptr.p_double[offsi+nx+j] = densew1.ptr.p_double[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update residuals (far radius is used)
|
|
*/
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
x0.ptr.p_double[j] = curxy.ptr.pp_double[i][j];
|
|
}
|
|
rbfv2_designmatrixgeneraterow(&kdnodes, &kdsplits, &cw, &ri, &kdroots, &kdboxmin, &kdboxmax, &cwrange, nx, ny, nh, levelidx, bf, rbfv2farradius(bf, _state), rowsperpoint, penalty, &x0, &calcbuf, &vr2, &voffs, &rowindexes, &rowvals, &cnt, _state);
|
|
for(j=0; j<=cnt-1; j++)
|
|
{
|
|
offsj = cwrange.ptr.p_int[levelidx]+(nx+ny)*rowindexes.ptr.p_int[j]+nx;
|
|
for(k=0; k<=rowsperpoint-1; k++)
|
|
{
|
|
val = rowvals.ptr.p_double[j*rowsperpoint+k];
|
|
for(k2=0; k2<=ny-1; k2++)
|
|
{
|
|
rhs.ptr.pp_double[i*rowsperpoint+k][k2] = rhs.ptr.pp_double[i*rowsperpoint+k][k2]-val*cw.ptr.p_double[offsj+k2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Model is built.
|
|
*
|
|
* Copy local variables by swapping, global ones (ScaleVec) are copied
|
|
* explicitly.
|
|
*/
|
|
s->bf = bf;
|
|
s->nh = nh;
|
|
ae_swap_vectors(&s->ri, &ri);
|
|
ae_swap_vectors(&s->kdroots, &kdroots);
|
|
ae_swap_vectors(&s->kdnodes, &kdnodes);
|
|
ae_swap_vectors(&s->kdsplits, &kdsplits);
|
|
ae_swap_vectors(&s->kdboxmin, &kdboxmin);
|
|
ae_swap_vectors(&s->kdboxmax, &kdboxmax);
|
|
ae_swap_vectors(&s->cw, &cw);
|
|
ae_swap_matrices(&s->v, &v);
|
|
ae_vector_set_length(&s->s, nx, _state);
|
|
for(i=0; i<=nx-1; i++)
|
|
{
|
|
s->s.ptr.p_double[i] = scalevec->ptr.p_double[i];
|
|
}
|
|
rep->terminationtype = 1;
|
|
|
|
/*
|
|
* Calculate maximum and RMS errors
|
|
*/
|
|
rep->maxerror = (double)(0);
|
|
rep->rmserror = (double)(0);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(rhs.ptr.pp_double[i*rowsperpoint][j], _state), _state);
|
|
rep->rmserror = rep->rmserror+ae_sqr(rhs.ptr.pp_double[i*rowsperpoint][j], _state);
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/(n*ny), _state);
|
|
|
|
/*
|
|
* Update progress reports
|
|
*/
|
|
*progress10000 = 10000;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: allocation
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.02.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv2alloc(ae_serializer* s, rbfv2model* model, ae_state *_state)
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* Data
|
|
*/
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
ae_serializer_alloc_entry(s);
|
|
allocrealarray(s, &model->ri, -1, _state);
|
|
allocrealarray(s, &model->s, -1, _state);
|
|
allocintegerarray(s, &model->kdroots, -1, _state);
|
|
allocintegerarray(s, &model->kdnodes, -1, _state);
|
|
allocrealarray(s, &model->kdsplits, -1, _state);
|
|
allocrealarray(s, &model->kdboxmin, -1, _state);
|
|
allocrealarray(s, &model->kdboxmax, -1, _state);
|
|
allocrealarray(s, &model->cw, -1, _state);
|
|
allocrealmatrix(s, &model->v, -1, -1, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: serialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.02.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv2serialize(ae_serializer* s, rbfv2model* model, ae_state *_state)
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* Data
|
|
*/
|
|
ae_serializer_serialize_int(s, model->nx, _state);
|
|
ae_serializer_serialize_int(s, model->ny, _state);
|
|
ae_serializer_serialize_int(s, model->nh, _state);
|
|
ae_serializer_serialize_int(s, model->bf, _state);
|
|
serializerealarray(s, &model->ri, -1, _state);
|
|
serializerealarray(s, &model->s, -1, _state);
|
|
serializeintegerarray(s, &model->kdroots, -1, _state);
|
|
serializeintegerarray(s, &model->kdnodes, -1, _state);
|
|
serializerealarray(s, &model->kdsplits, -1, _state);
|
|
serializerealarray(s, &model->kdboxmin, -1, _state);
|
|
serializerealarray(s, &model->kdboxmax, -1, _state);
|
|
serializerealarray(s, &model->cw, -1, _state);
|
|
serializerealmatrix(s, &model->v, -1, -1, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: unserialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.02.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv2unserialize(ae_serializer* s,
|
|
rbfv2model* model,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
|
|
_rbfv2model_clear(model);
|
|
|
|
|
|
/*
|
|
* 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);
|
|
rbfv2create(nx, ny, model, _state);
|
|
ae_serializer_unserialize_int(s, &model->nh, _state);
|
|
ae_serializer_unserialize_int(s, &model->bf, _state);
|
|
unserializerealarray(s, &model->ri, _state);
|
|
unserializerealarray(s, &model->s, _state);
|
|
unserializeintegerarray(s, &model->kdroots, _state);
|
|
unserializeintegerarray(s, &model->kdnodes, _state);
|
|
unserializerealarray(s, &model->kdsplits, _state);
|
|
unserializerealarray(s, &model->kdboxmin, _state);
|
|
unserializerealarray(s, &model->kdboxmax, _state);
|
|
unserializerealarray(s, &model->cw, _state);
|
|
unserializerealmatrix(s, &model->v, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Returns far radius for basis function type
|
|
*************************************************************************/
|
|
double rbfv2farradius(ae_int_t bf, ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
result = (double)(1);
|
|
if( bf==0 )
|
|
{
|
|
result = 5.0;
|
|
}
|
|
if( bf==1 )
|
|
{
|
|
result = (double)(3);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Returns near radius for basis function type
|
|
*************************************************************************/
|
|
double rbfv2nearradius(ae_int_t bf, ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
result = (double)(1);
|
|
if( bf==0 )
|
|
{
|
|
result = 3.0;
|
|
}
|
|
if( bf==1 )
|
|
{
|
|
result = (double)(3);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Returns basis function value.
|
|
Assumes that D2>=0
|
|
*************************************************************************/
|
|
double rbfv2basisfunc(ae_int_t bf, double d2, ae_state *_state)
|
|
{
|
|
double v;
|
|
double result;
|
|
|
|
|
|
result = (double)(0);
|
|
if( bf==0 )
|
|
{
|
|
result = ae_exp(-d2, _state);
|
|
return result;
|
|
}
|
|
if( bf==1 )
|
|
{
|
|
|
|
/*
|
|
* if D2<3:
|
|
* Exp(1)*Exp(-D2)*Exp(-1/(1-D2/9))
|
|
* else:
|
|
* 0
|
|
*/
|
|
v = 1-d2/9;
|
|
if( ae_fp_less_eq(v,(double)(0)) )
|
|
{
|
|
result = (double)(0);
|
|
return result;
|
|
}
|
|
result = 2.718281828459045*ae_exp(-d2, _state)*ae_exp(-1/v, _state);
|
|
return result;
|
|
}
|
|
ae_assert(ae_false, "RBFV2BasisFunc: unknown BF type", _state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Returns basis function value, first and second derivatives
|
|
Assumes that D2>=0
|
|
*************************************************************************/
|
|
void rbfv2basisfuncdiff2(ae_int_t bf,
|
|
double d2,
|
|
double* f,
|
|
double* df,
|
|
double* d2f,
|
|
ae_state *_state)
|
|
{
|
|
double v;
|
|
|
|
*f = 0;
|
|
*df = 0;
|
|
*d2f = 0;
|
|
|
|
if( bf==0 )
|
|
{
|
|
*f = ae_exp(-d2, _state);
|
|
*df = -*f;
|
|
*d2f = *f;
|
|
return;
|
|
}
|
|
if( bf==1 )
|
|
{
|
|
|
|
/*
|
|
* if D2<3:
|
|
* F = Exp(1)*Exp(-D2)*Exp(-1/(1-D2/9))
|
|
* dF = -F * [pow(D2/9-1,-2)/9 + 1]
|
|
* d2F = -dF * [pow(D2/9-1,-2)/9 + 1] + F*(2/81)*pow(D2/9-1,-3)
|
|
* else:
|
|
* 0
|
|
*/
|
|
v = 1-d2/9;
|
|
if( ae_fp_less_eq(v,(double)(0)) )
|
|
{
|
|
*f = (double)(0);
|
|
*df = (double)(0);
|
|
*d2f = (double)(0);
|
|
return;
|
|
}
|
|
*f = ae_exp((double)(1), _state)*ae_exp(-d2, _state)*ae_exp(-1/v, _state);
|
|
*df = -*f*(1/(9*v*v)+1);
|
|
*d2f = -*df*(1/(9*v*v)+1)+*f*((double)2/(double)81)/(v*v*v);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "RBFV2BasisFuncDiff2: unknown BF type", _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=1
|
|
(1-dimensional space).
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>1
|
|
*NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - X-coordinate, finite number
|
|
|
|
RESULT:
|
|
value of the model or 0.0 (as defined above)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfv2calc1(rbfv2model* s, double x0, ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
ae_assert(ae_isfinite(x0, _state), "RBFCalc1: invalid value for X0 (X0 is Inf)!", _state);
|
|
if( s->ny!=1||s->nx!=1 )
|
|
{
|
|
result = (double)(0);
|
|
return result;
|
|
}
|
|
result = s->v.ptr.pp_double[0][0]*x0-s->v.ptr.pp_double[0][1];
|
|
if( s->nh==0 )
|
|
{
|
|
return result;
|
|
}
|
|
rbfv2_allocatecalcbuffer(s, &s->calcbuf, _state);
|
|
s->calcbuf.x123.ptr.p_double[0] = x0;
|
|
rbfv2tscalcbuf(s, &s->calcbuf, &s->calcbuf.x123, &s->calcbuf.y123, _state);
|
|
result = s->calcbuf.y123.ptr.p_double[0];
|
|
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=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 rbfv2calc2(rbfv2model* s, double x0, double x1, ae_state *_state)
|
|
{
|
|
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 = (double)(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];
|
|
if( s->nh==0 )
|
|
{
|
|
return result;
|
|
}
|
|
rbfv2_allocatecalcbuffer(s, &s->calcbuf, _state);
|
|
s->calcbuf.x123.ptr.p_double[0] = x0;
|
|
s->calcbuf.x123.ptr.p_double[1] = x1;
|
|
rbfv2tscalcbuf(s, &s->calcbuf, &s->calcbuf.x123, &s->calcbuf.y123, _state);
|
|
result = s->calcbuf.y123.ptr.p_double[0];
|
|
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 rbfv2calc3(rbfv2model* s,
|
|
double x0,
|
|
double x1,
|
|
double x2,
|
|
ae_state *_state)
|
|
{
|
|
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 = (double)(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][3];
|
|
if( s->nh==0 )
|
|
{
|
|
return result;
|
|
}
|
|
rbfv2_allocatecalcbuffer(s, &s->calcbuf, _state);
|
|
s->calcbuf.x123.ptr.p_double[0] = x0;
|
|
s->calcbuf.x123.ptr.p_double[1] = x1;
|
|
s->calcbuf.x123.ptr.p_double[2] = x2;
|
|
rbfv2tscalcbuf(s, &s->calcbuf, &s->calcbuf.x123, &s->calcbuf.y123, _state);
|
|
result = s->calcbuf.y123.ptr.p_double[0];
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 rbfv2calcbuf(rbfv2model* s,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
rbfv2tscalcbuf(s, &s->calcbuf, x, y, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the given point, using
|
|
external buffer object (internal temporaries of RBF model are not
|
|
modified).
|
|
|
|
This function allows to use same RBF model object in different threads,
|
|
assuming that different threads use different instances of buffer
|
|
structure.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, may be shared between different threads
|
|
Buf - buffer object created for this particular instance of RBF
|
|
model with rbfcreatecalcbuffer().
|
|
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 rbfv2tscalcbuf(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t levelidx;
|
|
double rcur;
|
|
double rquery2;
|
|
double invrc2;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
|
|
|
|
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);
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
|
|
/*
|
|
* Handle linear term
|
|
*/
|
|
if( y->cnt<ny )
|
|
{
|
|
ae_vector_set_length(y, ny, _state);
|
|
}
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
y->ptr.p_double[i] = s->v.ptr.pp_double[i][nx];
|
|
for(j=0; j<=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->nh==0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Handle nonlinear term
|
|
*/
|
|
rbfv2_allocatecalcbuffer(s, buf, _state);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
buf->x.ptr.p_double[j] = x->ptr.p_double[j]/s->s.ptr.p_double[j];
|
|
}
|
|
for(levelidx=0; levelidx<=s->nh-1; levelidx++)
|
|
{
|
|
|
|
/*
|
|
* Prepare fields of Buf required by PartialCalcRec()
|
|
*/
|
|
buf->curdist2 = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
buf->curboxmin.ptr.p_double[j] = s->kdboxmin.ptr.p_double[j];
|
|
buf->curboxmax.ptr.p_double[j] = s->kdboxmax.ptr.p_double[j];
|
|
if( ae_fp_less(buf->x.ptr.p_double[j],buf->curboxmin.ptr.p_double[j]) )
|
|
{
|
|
buf->curdist2 = buf->curdist2+ae_sqr(buf->curboxmin.ptr.p_double[j]-buf->x.ptr.p_double[j], _state);
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater(buf->x.ptr.p_double[j],buf->curboxmax.ptr.p_double[j]) )
|
|
{
|
|
buf->curdist2 = buf->curdist2+ae_sqr(buf->x.ptr.p_double[j]-buf->curboxmax.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Call PartialCalcRec()
|
|
*/
|
|
rcur = s->ri.ptr.p_double[levelidx];
|
|
invrc2 = 1/(rcur*rcur);
|
|
rquery2 = ae_sqr(rcur*rbfv2farradius(s->bf, _state), _state);
|
|
rbfv2_partialcalcrec(s, buf, s->kdroots.ptr.p_int[levelidx], invrc2, rquery2, &buf->x, y, _state);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
NOTE: as a special exception, this function supports unordered arrays X0
|
|
and X1. However, future versions may be more efficient for X0/X1
|
|
ordered by ascending.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv2gridcalc2(rbfv2model* 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 dummyx2;
|
|
ae_vector dummyx3;
|
|
ae_vector dummyflag;
|
|
ae_vector p01;
|
|
ae_vector p11;
|
|
ae_vector p2;
|
|
ae_vector vy;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&cpx0, 0, sizeof(cpx0));
|
|
memset(&cpx1, 0, sizeof(cpx1));
|
|
memset(&dummyx2, 0, sizeof(dummyx2));
|
|
memset(&dummyx3, 0, sizeof(dummyx3));
|
|
memset(&dummyflag, 0, sizeof(dummyflag));
|
|
memset(&p01, 0, sizeof(p01));
|
|
memset(&p11, 0, sizeof(p11));
|
|
memset(&p2, 0, sizeof(p2));
|
|
memset(&vy, 0, sizeof(vy));
|
|
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(&dummyx2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dummyx3, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dummyflag, 0, DT_BOOL, _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_vector_init(&vy, 0, DT_REAL, _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] = (double)(0);
|
|
}
|
|
}
|
|
if( s->ny!=1||s->nx!=2 )
|
|
{
|
|
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);
|
|
ae_vector_set_length(&dummyx2, 1, _state);
|
|
dummyx2.ptr.p_double[0] = (double)(0);
|
|
ae_vector_set_length(&dummyx3, 1, _state);
|
|
dummyx3.ptr.p_double[0] = (double)(0);
|
|
ae_vector_set_length(&vy, n0*n1, _state);
|
|
rbfv2gridcalcvx(s, &cpx0, n0, &cpx1, n1, &dummyx2, 1, &dummyx3, 1, &dummyflag, ae_false, &vy, _state);
|
|
for(i=0; i<=n0-1; i++)
|
|
{
|
|
for(j=0; j<=n1-1; j++)
|
|
{
|
|
y->ptr.pp_double[i][j] = vy.ptr.p_double[i+j*n0];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is used to perform gridded calculation for 2D, 3D or 4D
|
|
problems. It accepts parameters X0...X3 and counters N0...N3. If RBF model
|
|
has dimensionality less than 4, corresponding arrays should contain just
|
|
one element equal to zero, and corresponding N's should be equal to 1.
|
|
|
|
NOTE: array Y should be preallocated by caller.
|
|
|
|
-- ALGLIB --
|
|
Copyright 12.07.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv2gridcalcvx(rbfv2model* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* x3,
|
|
ae_int_t n3,
|
|
/* Boolean */ ae_vector* flagy,
|
|
ae_bool sparsey,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_vector tx;
|
|
ae_vector ty;
|
|
ae_vector z;
|
|
ae_int_t dstoffs;
|
|
ae_int_t dummy;
|
|
rbfv2gridcalcbuffer bufseedv2;
|
|
ae_shared_pool bufpool;
|
|
ae_int_t rowidx;
|
|
ae_int_t rowcnt;
|
|
double v;
|
|
double rcur;
|
|
ae_int_t levelidx;
|
|
double searchradius2;
|
|
ae_int_t ntrials;
|
|
double avgfuncpernode;
|
|
hqrndstate rs;
|
|
ae_vector blocks0;
|
|
ae_vector blocks1;
|
|
ae_vector blocks2;
|
|
ae_vector blocks3;
|
|
ae_int_t blockscnt0;
|
|
ae_int_t blockscnt1;
|
|
ae_int_t blockscnt2;
|
|
ae_int_t blockscnt3;
|
|
double blockwidth0;
|
|
double blockwidth1;
|
|
double blockwidth2;
|
|
double blockwidth3;
|
|
ae_int_t maxblocksize;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&tx, 0, sizeof(tx));
|
|
memset(&ty, 0, sizeof(ty));
|
|
memset(&z, 0, sizeof(z));
|
|
memset(&bufseedv2, 0, sizeof(bufseedv2));
|
|
memset(&bufpool, 0, sizeof(bufpool));
|
|
memset(&rs, 0, sizeof(rs));
|
|
memset(&blocks0, 0, sizeof(blocks0));
|
|
memset(&blocks1, 0, sizeof(blocks1));
|
|
memset(&blocks2, 0, sizeof(blocks2));
|
|
memset(&blocks3, 0, sizeof(blocks3));
|
|
ae_vector_init(&tx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&ty, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
|
|
_rbfv2gridcalcbuffer_init(&bufseedv2, _state, ae_true);
|
|
ae_shared_pool_init(&bufpool, _state, ae_true);
|
|
_hqrndstate_init(&rs, _state, ae_true);
|
|
ae_vector_init(&blocks0, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&blocks1, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&blocks2, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&blocks3, 0, DT_INT, _state, ae_true);
|
|
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
hqrndseed(532, 54734, &rs, _state);
|
|
|
|
/*
|
|
* Perform integrity checks
|
|
*/
|
|
ae_assert(s->nx==2||s->nx==3, "RBFGridCalcVX: integrity check failed", _state);
|
|
ae_assert(s->nx>=4||((x3->cnt>=1&&ae_fp_eq(x3->ptr.p_double[0],(double)(0)))&&n3==1), "RBFGridCalcVX: integrity check failed", _state);
|
|
ae_assert(s->nx>=3||((x2->cnt>=1&&ae_fp_eq(x2->ptr.p_double[0],(double)(0)))&&n2==1), "RBFGridCalcVX: integrity check failed", _state);
|
|
ae_assert(s->nx>=2||((x1->cnt>=1&&ae_fp_eq(x1->ptr.p_double[0],(double)(0)))&&n1==1), "RBFGridCalcVX: integrity check failed", _state);
|
|
|
|
/*
|
|
* Allocate arrays
|
|
*/
|
|
ae_assert(s->nx<=4, "RBFGridCalcVX: integrity check failed", _state);
|
|
ae_vector_set_length(&z, ny, _state);
|
|
ae_vector_set_length(&tx, 4, _state);
|
|
ae_vector_set_length(&ty, ny, _state);
|
|
|
|
/*
|
|
* Calculate linear term
|
|
*/
|
|
rowcnt = n1*n2*n3;
|
|
for(rowidx=0; rowidx<=rowcnt-1; rowidx++)
|
|
{
|
|
|
|
/*
|
|
* Calculate TX - current position
|
|
*/
|
|
k = rowidx;
|
|
tx.ptr.p_double[0] = (double)(0);
|
|
tx.ptr.p_double[1] = x1->ptr.p_double[k%n1];
|
|
k = k/n1;
|
|
tx.ptr.p_double[2] = x2->ptr.p_double[k%n2];
|
|
k = k/n2;
|
|
tx.ptr.p_double[3] = x3->ptr.p_double[k%n3];
|
|
k = k/n3;
|
|
ae_assert(k==0, "RBFGridCalcVX: integrity check failed", _state);
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
v = s->v.ptr.pp_double[j][nx];
|
|
for(k=1; k<=nx-1; k++)
|
|
{
|
|
v = v+tx.ptr.p_double[k]*s->v.ptr.pp_double[j][k];
|
|
}
|
|
z.ptr.p_double[j] = v;
|
|
}
|
|
for(i=0; i<=n0-1; i++)
|
|
{
|
|
dstoffs = ny*(rowidx*n0+i);
|
|
if( sparsey&&!flagy->ptr.p_bool[rowidx*n0+i] )
|
|
{
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j+dstoffs] = (double)(0);
|
|
}
|
|
continue;
|
|
}
|
|
v = x0->ptr.p_double[i];
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j+dstoffs] = z.ptr.p_double[j]+v*s->v.ptr.pp_double[j][0];
|
|
}
|
|
}
|
|
}
|
|
if( s->nh==0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process RBF terms, layer by layer
|
|
*/
|
|
for(levelidx=0; levelidx<=s->nh-1; levelidx++)
|
|
{
|
|
rcur = s->ri.ptr.p_double[levelidx];
|
|
blockwidth0 = (double)(1);
|
|
blockwidth1 = (double)(1);
|
|
blockwidth2 = (double)(1);
|
|
blockwidth3 = (double)(1);
|
|
if( nx>=1 )
|
|
{
|
|
blockwidth0 = rcur*s->s.ptr.p_double[0];
|
|
}
|
|
if( nx>=2 )
|
|
{
|
|
blockwidth1 = rcur*s->s.ptr.p_double[1];
|
|
}
|
|
if( nx>=3 )
|
|
{
|
|
blockwidth2 = rcur*s->s.ptr.p_double[2];
|
|
}
|
|
if( nx>=4 )
|
|
{
|
|
blockwidth3 = rcur*s->s.ptr.p_double[3];
|
|
}
|
|
maxblocksize = 8;
|
|
|
|
/*
|
|
* Group grid nodes into blocks according to current radius
|
|
*/
|
|
ae_vector_set_length(&blocks0, n0+1, _state);
|
|
blockscnt0 = 0;
|
|
blocks0.ptr.p_int[0] = 0;
|
|
for(i=1; i<=n0-1; i++)
|
|
{
|
|
if( ae_fp_greater(x0->ptr.p_double[i]-x0->ptr.p_double[blocks0.ptr.p_int[blockscnt0]],blockwidth0)||i-blocks0.ptr.p_int[blockscnt0]>=maxblocksize )
|
|
{
|
|
inc(&blockscnt0, _state);
|
|
blocks0.ptr.p_int[blockscnt0] = i;
|
|
}
|
|
}
|
|
inc(&blockscnt0, _state);
|
|
blocks0.ptr.p_int[blockscnt0] = n0;
|
|
ae_vector_set_length(&blocks1, n1+1, _state);
|
|
blockscnt1 = 0;
|
|
blocks1.ptr.p_int[0] = 0;
|
|
for(i=1; i<=n1-1; i++)
|
|
{
|
|
if( ae_fp_greater(x1->ptr.p_double[i]-x1->ptr.p_double[blocks1.ptr.p_int[blockscnt1]],blockwidth1)||i-blocks1.ptr.p_int[blockscnt1]>=maxblocksize )
|
|
{
|
|
inc(&blockscnt1, _state);
|
|
blocks1.ptr.p_int[blockscnt1] = i;
|
|
}
|
|
}
|
|
inc(&blockscnt1, _state);
|
|
blocks1.ptr.p_int[blockscnt1] = n1;
|
|
ae_vector_set_length(&blocks2, n2+1, _state);
|
|
blockscnt2 = 0;
|
|
blocks2.ptr.p_int[0] = 0;
|
|
for(i=1; i<=n2-1; i++)
|
|
{
|
|
if( ae_fp_greater(x2->ptr.p_double[i]-x2->ptr.p_double[blocks2.ptr.p_int[blockscnt2]],blockwidth2)||i-blocks2.ptr.p_int[blockscnt2]>=maxblocksize )
|
|
{
|
|
inc(&blockscnt2, _state);
|
|
blocks2.ptr.p_int[blockscnt2] = i;
|
|
}
|
|
}
|
|
inc(&blockscnt2, _state);
|
|
blocks2.ptr.p_int[blockscnt2] = n2;
|
|
ae_vector_set_length(&blocks3, n3+1, _state);
|
|
blockscnt3 = 0;
|
|
blocks3.ptr.p_int[0] = 0;
|
|
for(i=1; i<=n3-1; i++)
|
|
{
|
|
if( ae_fp_greater(x3->ptr.p_double[i]-x3->ptr.p_double[blocks3.ptr.p_int[blockscnt3]],blockwidth3)||i-blocks3.ptr.p_int[blockscnt3]>=maxblocksize )
|
|
{
|
|
inc(&blockscnt3, _state);
|
|
blocks3.ptr.p_int[blockscnt3] = i;
|
|
}
|
|
}
|
|
inc(&blockscnt3, _state);
|
|
blocks3.ptr.p_int[blockscnt3] = n3;
|
|
|
|
/*
|
|
* Prepare seed for shared pool
|
|
*/
|
|
rbfv2_allocatecalcbuffer(s, &bufseedv2.calcbuf, _state);
|
|
ae_shared_pool_set_seed(&bufpool, &bufseedv2, sizeof(bufseedv2), _rbfv2gridcalcbuffer_init, _rbfv2gridcalcbuffer_init_copy, _rbfv2gridcalcbuffer_destroy, _state);
|
|
|
|
/*
|
|
* Determine average number of neighbor per node
|
|
*/
|
|
searchradius2 = ae_sqr(rcur*rbfv2farradius(s->bf, _state), _state);
|
|
ntrials = 100;
|
|
avgfuncpernode = 0.0;
|
|
for(i=0; i<=ntrials-1; i++)
|
|
{
|
|
tx.ptr.p_double[0] = x0->ptr.p_double[hqrnduniformi(&rs, n0, _state)];
|
|
tx.ptr.p_double[1] = x1->ptr.p_double[hqrnduniformi(&rs, n1, _state)];
|
|
tx.ptr.p_double[2] = x2->ptr.p_double[hqrnduniformi(&rs, n2, _state)];
|
|
tx.ptr.p_double[3] = x3->ptr.p_double[hqrnduniformi(&rs, n3, _state)];
|
|
rbfv2_preparepartialquery(&tx, &s->kdboxmin, &s->kdboxmax, nx, &bufseedv2.calcbuf, &dummy, _state);
|
|
avgfuncpernode = avgfuncpernode+(double)rbfv2_partialcountrec(&s->kdnodes, &s->kdsplits, &s->cw, nx, ny, &bufseedv2.calcbuf, s->kdroots.ptr.p_int[levelidx], searchradius2, &tx, _state)/(double)ntrials;
|
|
}
|
|
|
|
/*
|
|
* Perform calculation in multithreaded mode
|
|
*/
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, &blocks0, 0, blockscnt0, &blocks1, 0, blockscnt1, &blocks2, 0, blockscnt2, &blocks3, 0, blockscnt3, flagy, sparsey, levelidx, avgfuncpernode, &bufpool, y, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
void rbfv2partialgridcalcrec(rbfv2model* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* x3,
|
|
ae_int_t n3,
|
|
/* Integer */ ae_vector* blocks0,
|
|
ae_int_t block0a,
|
|
ae_int_t block0b,
|
|
/* Integer */ ae_vector* blocks1,
|
|
ae_int_t block1a,
|
|
ae_int_t block1b,
|
|
/* Integer */ ae_vector* blocks2,
|
|
ae_int_t block2a,
|
|
ae_int_t block2b,
|
|
/* Integer */ ae_vector* blocks3,
|
|
ae_int_t block3a,
|
|
ae_int_t block3b,
|
|
/* Boolean */ ae_vector* flagy,
|
|
ae_bool sparsey,
|
|
ae_int_t levelidx,
|
|
double avgfuncpernode,
|
|
ae_shared_pool* bufpool,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
ae_int_t k;
|
|
ae_int_t l;
|
|
ae_int_t blkidx;
|
|
ae_int_t blkcnt;
|
|
ae_int_t nodeidx;
|
|
ae_int_t nodescnt;
|
|
ae_int_t rowidx;
|
|
ae_int_t rowscnt;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
ae_int_t i2;
|
|
ae_int_t i3;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t j2;
|
|
ae_int_t j3;
|
|
double rcur;
|
|
double invrc2;
|
|
double rquery2;
|
|
double rfar2;
|
|
ae_int_t dstoffs;
|
|
ae_int_t srcoffs;
|
|
ae_int_t dummy;
|
|
double rowwidth;
|
|
double maxrowwidth;
|
|
double problemcost;
|
|
ae_int_t maxbs;
|
|
ae_int_t midpoint;
|
|
ae_bool emptyrow;
|
|
rbfv2gridcalcbuffer *buf;
|
|
ae_smart_ptr _buf;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_buf, 0, sizeof(_buf));
|
|
ae_smart_ptr_init(&_buf, (void**)&buf, _state, ae_true);
|
|
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
|
|
/*
|
|
* Integrity checks
|
|
*/
|
|
ae_assert(s->nx==2||s->nx==3, "RBFV2PartialGridCalcRec: integrity check failed", _state);
|
|
|
|
/*
|
|
* Try to split large problem
|
|
*/
|
|
problemcost = s->ny*2*(avgfuncpernode+1);
|
|
problemcost = problemcost*(blocks0->ptr.p_int[block0b]-blocks0->ptr.p_int[block0a]);
|
|
problemcost = problemcost*(blocks1->ptr.p_int[block1b]-blocks1->ptr.p_int[block1a]);
|
|
problemcost = problemcost*(blocks2->ptr.p_int[block2b]-blocks2->ptr.p_int[block2a]);
|
|
problemcost = problemcost*(blocks3->ptr.p_int[block3b]-blocks3->ptr.p_int[block3a]);
|
|
maxbs = 0;
|
|
maxbs = ae_maxint(maxbs, block0b-block0a, _state);
|
|
maxbs = ae_maxint(maxbs, block1b-block1a, _state);
|
|
maxbs = ae_maxint(maxbs, block2b-block2a, _state);
|
|
maxbs = ae_maxint(maxbs, block3b-block3a, _state);
|
|
if( ae_fp_greater_eq(problemcost*rbfv2_complexitymultiplier,smpactivationlevel(_state)) )
|
|
{
|
|
if( _trypexec_rbfv2partialgridcalcrec(s,x0,n0,x1,n1,x2,n2,x3,n3,blocks0,block0a,block0b,blocks1,block1a,block1b,blocks2,block2a,block2b,blocks3,block3a,block3b,flagy,sparsey,levelidx,avgfuncpernode,bufpool,y, _state) )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
if( ae_fp_greater_eq(problemcost*rbfv2_complexitymultiplier,spawnlevel(_state))&&maxbs>=2 )
|
|
{
|
|
if( block0b-block0a==maxbs )
|
|
{
|
|
midpoint = block0a+maxbs/2;
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, block0a, midpoint, blocks1, block1a, block1b, blocks2, block2a, block2b, blocks3, block3a, block3b, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, midpoint, block0b, blocks1, block1a, block1b, blocks2, block2a, block2b, blocks3, block3a, block3b, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( block1b-block1a==maxbs )
|
|
{
|
|
midpoint = block1a+maxbs/2;
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, block0a, block0b, blocks1, block1a, midpoint, blocks2, block2a, block2b, blocks3, block3a, block3b, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, block0a, block0b, blocks1, midpoint, block1b, blocks2, block2a, block2b, blocks3, block3a, block3b, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( block2b-block2a==maxbs )
|
|
{
|
|
midpoint = block2a+maxbs/2;
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, block0a, block0b, blocks1, block1a, block1b, blocks2, block2a, midpoint, blocks3, block3a, block3b, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, block0a, block0b, blocks1, block1a, block1b, blocks2, midpoint, block2b, blocks3, block3a, block3b, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( block3b-block3a==maxbs )
|
|
{
|
|
midpoint = block3a+maxbs/2;
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, block0a, block0b, blocks1, block1a, block1b, blocks2, block2a, block2b, blocks3, block3a, midpoint, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
rbfv2partialgridcalcrec(s, x0, n0, x1, n1, x2, n2, x3, n3, blocks0, block0a, block0b, blocks1, block1a, block1b, blocks2, block2a, block2b, blocks3, midpoint, block3b, flagy, sparsey, levelidx, avgfuncpernode, bufpool, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "RBFV2PartialGridCalcRec: integrity check failed", _state);
|
|
}
|
|
|
|
/*
|
|
* Retrieve buffer object from pool (it will be returned later)
|
|
*/
|
|
ae_shared_pool_retrieve(bufpool, &_buf, _state);
|
|
|
|
/*
|
|
* Calculate RBF model
|
|
*/
|
|
ae_assert(nx<=4, "RBFV2PartialGridCalcRec: integrity check failed", _state);
|
|
ae_vector_set_length(&buf->tx, 4, _state);
|
|
ae_vector_set_length(&buf->cx, 4, _state);
|
|
ae_vector_set_length(&buf->ty, ny, _state);
|
|
rcur = s->ri.ptr.p_double[levelidx];
|
|
invrc2 = 1/(rcur*rcur);
|
|
blkcnt = (block3b-block3a)*(block2b-block2a)*(block1b-block1a)*(block0b-block0a);
|
|
for(blkidx=0; blkidx<=blkcnt-1; blkidx++)
|
|
{
|
|
|
|
/*
|
|
* Select block (I0,I1,I2,I3).
|
|
*
|
|
* NOTE: for problems with NX<4 corresponding I_? are zero.
|
|
*/
|
|
k = blkidx;
|
|
i0 = block0a+k%(block0b-block0a);
|
|
k = k/(block0b-block0a);
|
|
i1 = block1a+k%(block1b-block1a);
|
|
k = k/(block1b-block1a);
|
|
i2 = block2a+k%(block2b-block2a);
|
|
k = k/(block2b-block2a);
|
|
i3 = block3a+k%(block3b-block3a);
|
|
k = k/(block3b-block3a);
|
|
ae_assert(k==0, "RBFV2PartialGridCalcRec: integrity check failed", _state);
|
|
|
|
/*
|
|
* We partitioned grid into blocks and selected block with
|
|
* index (I0,I1,I2,I3). This block is a 4D cube (some dimensions
|
|
* may be zero) of nodes with indexes (J0,J1,J2,J3), which is
|
|
* further partitioned into a set of rows, each row corresponding
|
|
* to indexes J1...J3 being fixed.
|
|
*
|
|
* We process block row by row, and each row may be handled
|
|
* by either "generic" (nodes are processed separately) or
|
|
* batch algorithm (that's the reason to use rows, after all).
|
|
*
|
|
*
|
|
* Process nodes of the block
|
|
*/
|
|
rowscnt = (blocks3->ptr.p_int[i3+1]-blocks3->ptr.p_int[i3])*(blocks2->ptr.p_int[i2+1]-blocks2->ptr.p_int[i2])*(blocks1->ptr.p_int[i1+1]-blocks1->ptr.p_int[i1]);
|
|
for(rowidx=0; rowidx<=rowscnt-1; rowidx++)
|
|
{
|
|
|
|
/*
|
|
* Find out node indexes (*,J1,J2,J3).
|
|
*
|
|
* NOTE: for problems with NX<4 corresponding J_? are zero.
|
|
*/
|
|
k = rowidx;
|
|
j1 = blocks1->ptr.p_int[i1]+k%(blocks1->ptr.p_int[i1+1]-blocks1->ptr.p_int[i1]);
|
|
k = k/(blocks1->ptr.p_int[i1+1]-blocks1->ptr.p_int[i1]);
|
|
j2 = blocks2->ptr.p_int[i2]+k%(blocks2->ptr.p_int[i2+1]-blocks2->ptr.p_int[i2]);
|
|
k = k/(blocks2->ptr.p_int[i2+1]-blocks2->ptr.p_int[i2]);
|
|
j3 = blocks3->ptr.p_int[i3]+k%(blocks3->ptr.p_int[i3+1]-blocks3->ptr.p_int[i3]);
|
|
k = k/(blocks3->ptr.p_int[i3+1]-blocks3->ptr.p_int[i3]);
|
|
ae_assert(k==0, "RBFV2PartialGridCalcRec: integrity check failed", _state);
|
|
|
|
/*
|
|
* Analyze row, skip completely empty rows
|
|
*/
|
|
nodescnt = blocks0->ptr.p_int[i0+1]-blocks0->ptr.p_int[i0];
|
|
srcoffs = blocks0->ptr.p_int[i0]+(j1+(j2+j3*n2)*n1)*n0;
|
|
emptyrow = ae_true;
|
|
for(nodeidx=0; nodeidx<=nodescnt-1; nodeidx++)
|
|
{
|
|
emptyrow = emptyrow&&(sparsey&&!flagy->ptr.p_bool[srcoffs+nodeidx]);
|
|
}
|
|
if( emptyrow )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Process row - use either "batch" (rowsize>1) or "generic"
|
|
* (row size is 1) algorithm.
|
|
*
|
|
* NOTE: "generic" version may also be used as fallback code for
|
|
* situations when we do not want to use batch code.
|
|
*/
|
|
maxrowwidth = 0.5*rbfv2nearradius(s->bf, _state)*rcur*s->s.ptr.p_double[0];
|
|
rowwidth = x0->ptr.p_double[blocks0->ptr.p_int[i0+1]-1]-x0->ptr.p_double[blocks0->ptr.p_int[i0]];
|
|
if( nodescnt>1&&ae_fp_less_eq(rowwidth,maxrowwidth) )
|
|
{
|
|
|
|
/*
|
|
* "Batch" code which processes entire row at once, saving
|
|
* some time in kd-tree search code.
|
|
*/
|
|
rquery2 = ae_sqr(rcur*rbfv2farradius(s->bf, _state)+0.5*rowwidth/s->s.ptr.p_double[0], _state);
|
|
rfar2 = ae_sqr(rcur*rbfv2farradius(s->bf, _state), _state);
|
|
j0 = blocks0->ptr.p_int[i0];
|
|
if( nx>0 )
|
|
{
|
|
buf->cx.ptr.p_double[0] = (x0->ptr.p_double[j0]+0.5*rowwidth)/s->s.ptr.p_double[0];
|
|
}
|
|
if( nx>1 )
|
|
{
|
|
buf->cx.ptr.p_double[1] = x1->ptr.p_double[j1]/s->s.ptr.p_double[1];
|
|
}
|
|
if( nx>2 )
|
|
{
|
|
buf->cx.ptr.p_double[2] = x2->ptr.p_double[j2]/s->s.ptr.p_double[2];
|
|
}
|
|
if( nx>3 )
|
|
{
|
|
buf->cx.ptr.p_double[3] = x3->ptr.p_double[j3]/s->s.ptr.p_double[3];
|
|
}
|
|
srcoffs = j0+(j1+(j2+j3*n2)*n1)*n0;
|
|
dstoffs = ny*srcoffs;
|
|
rvectorsetlengthatleast(&buf->rx, nodescnt, _state);
|
|
bvectorsetlengthatleast(&buf->rf, nodescnt, _state);
|
|
rvectorsetlengthatleast(&buf->ry, nodescnt*ny, _state);
|
|
for(nodeidx=0; nodeidx<=nodescnt-1; nodeidx++)
|
|
{
|
|
buf->rx.ptr.p_double[nodeidx] = x0->ptr.p_double[j0+nodeidx]/s->s.ptr.p_double[0];
|
|
buf->rf.ptr.p_bool[nodeidx] = !sparsey||flagy->ptr.p_bool[srcoffs+nodeidx];
|
|
}
|
|
for(k=0; k<=nodescnt*ny-1; k++)
|
|
{
|
|
buf->ry.ptr.p_double[k] = (double)(0);
|
|
}
|
|
rbfv2_preparepartialquery(&buf->cx, &s->kdboxmin, &s->kdboxmax, nx, &buf->calcbuf, &dummy, _state);
|
|
rbfv2_partialrowcalcrec(s, &buf->calcbuf, s->kdroots.ptr.p_int[levelidx], invrc2, rquery2, rfar2, &buf->cx, &buf->rx, &buf->rf, nodescnt, &buf->ry, _state);
|
|
for(k=0; k<=nodescnt*ny-1; k++)
|
|
{
|
|
y->ptr.p_double[dstoffs+k] = y->ptr.p_double[dstoffs+k]+buf->ry.ptr.p_double[k];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* "Generic" code. Although we usually move here
|
|
* only when NodesCnt=1, we still use a loop on
|
|
* NodeIdx just to be able to use this branch as
|
|
* fallback code without any modifications.
|
|
*/
|
|
rquery2 = ae_sqr(rcur*rbfv2farradius(s->bf, _state), _state);
|
|
for(nodeidx=0; nodeidx<=nodescnt-1; nodeidx++)
|
|
{
|
|
|
|
/*
|
|
* Prepare TX - current point
|
|
*/
|
|
j0 = blocks0->ptr.p_int[i0]+nodeidx;
|
|
if( nx>0 )
|
|
{
|
|
buf->tx.ptr.p_double[0] = x0->ptr.p_double[j0]/s->s.ptr.p_double[0];
|
|
}
|
|
if( nx>1 )
|
|
{
|
|
buf->tx.ptr.p_double[1] = x1->ptr.p_double[j1]/s->s.ptr.p_double[1];
|
|
}
|
|
if( nx>2 )
|
|
{
|
|
buf->tx.ptr.p_double[2] = x2->ptr.p_double[j2]/s->s.ptr.p_double[2];
|
|
}
|
|
if( nx>3 )
|
|
{
|
|
buf->tx.ptr.p_double[3] = x3->ptr.p_double[j3]/s->s.ptr.p_double[3];
|
|
}
|
|
|
|
/*
|
|
* Evaluate and add to Y
|
|
*/
|
|
srcoffs = j0+(j1+(j2+j3*n2)*n1)*n0;
|
|
dstoffs = ny*srcoffs;
|
|
for(l=0; l<=ny-1; l++)
|
|
{
|
|
buf->ty.ptr.p_double[l] = (double)(0);
|
|
}
|
|
if( !sparsey||flagy->ptr.p_bool[srcoffs] )
|
|
{
|
|
rbfv2_preparepartialquery(&buf->tx, &s->kdboxmin, &s->kdboxmax, nx, &buf->calcbuf, &dummy, _state);
|
|
rbfv2_partialcalcrec(s, &buf->calcbuf, s->kdroots.ptr.p_int[levelidx], invrc2, rquery2, &buf->tx, &buf->ty, _state);
|
|
}
|
|
for(l=0; l<=ny-1; l++)
|
|
{
|
|
y->ptr.p_double[dstoffs+l] = y->ptr.p_double[dstoffs+l]+buf->ty.ptr.p_double[l];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Recycle buffer object back to pool
|
|
*/
|
|
ae_shared_pool_recycle(bufpool, &_buf, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_rbfv2partialgridcalcrec(rbfv2model* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* x3,
|
|
ae_int_t n3,
|
|
/* Integer */ ae_vector* blocks0,
|
|
ae_int_t block0a,
|
|
ae_int_t block0b,
|
|
/* Integer */ ae_vector* blocks1,
|
|
ae_int_t block1a,
|
|
ae_int_t block1b,
|
|
/* Integer */ ae_vector* blocks2,
|
|
ae_int_t block2a,
|
|
ae_int_t block2b,
|
|
/* Integer */ ae_vector* blocks3,
|
|
ae_int_t block3a,
|
|
ae_int_t block3b,
|
|
/* Boolean */ ae_vector* flagy,
|
|
ae_bool sparsey,
|
|
ae_int_t levelidx,
|
|
double avgfuncpernode,
|
|
ae_shared_pool* bufpool,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 NX columns - radii, per dimension
|
|
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 rbfv2unpack(rbfv2model* 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 ncactual;
|
|
|
|
*nx = 0;
|
|
*ny = 0;
|
|
ae_matrix_clear(xwr);
|
|
*nc = 0;
|
|
ae_matrix_clear(v);
|
|
|
|
*nx = s->nx;
|
|
*ny = s->ny;
|
|
*nc = 0;
|
|
|
|
/*
|
|
* 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));
|
|
}
|
|
|
|
/*
|
|
* Fill XWR
|
|
*/
|
|
ae_assert(s->cw.cnt%(s->nx+s->ny)==0, "RBFV2Unpack: integrity error", _state);
|
|
*nc = s->cw.cnt/(s->nx+s->ny);
|
|
ncactual = 0;
|
|
if( *nc>0 )
|
|
{
|
|
ae_matrix_set_length(xwr, *nc, s->nx+s->ny+s->nx, _state);
|
|
for(i=0; i<=s->nh-1; i++)
|
|
{
|
|
rbfv2_partialunpackrec(&s->kdnodes, &s->kdsplits, &s->cw, &s->s, s->nx, s->ny, s->kdroots.ptr.p_int[i], s->ri.ptr.p_double[i], xwr, &ncactual, _state);
|
|
}
|
|
}
|
|
ae_assert(*nc==ncactual, "RBFV2Unpack: integrity error", _state);
|
|
}
|
|
|
|
|
|
static ae_bool rbfv2_rbfv2buildlinearmodel(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
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);
|
|
memset(&tmpy, 0, sizeof(tmpy));
|
|
memset(&a, 0, sizeof(a));
|
|
memset(&shifting, 0, sizeof(shifting));
|
|
memset(&c, 0, sizeof(c));
|
|
memset(&rep, 0, sizeof(rep));
|
|
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(nx>0, "BuildLinearModel: NX<=0", _state);
|
|
ae_assert(ny>0, "BuildLinearModel: NY<=0", _state);
|
|
|
|
/*
|
|
* Handle degenerate case (N=0)
|
|
*/
|
|
result = ae_true;
|
|
ae_matrix_set_length(v, ny, nx+1, _state);
|
|
if( n==0 )
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(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, nx+1, _state);
|
|
ae_vector_set_length(&shifting, nx, _state);
|
|
scaling = (double)(0);
|
|
for(i=0; i<=nx-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,(double)(0)) )
|
|
{
|
|
scaling = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
scaling = 0.5*scaling;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=nx-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][nx] = (double)(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, nx+1, &info, &c, &rep, _state);
|
|
if( info<=0 )
|
|
{
|
|
result = ae_false;
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = c.ptr.p_double[j]/scaling;
|
|
}
|
|
v->ptr.pp_double[i][nx] = c.ptr.p_double[nx];
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][nx] = v->ptr.pp_double[i][nx]-shifting.ptr.p_double[j]*v->ptr.pp_double[i][j];
|
|
}
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
for(k=0; k<=nx-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][nx];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Constant model, very simple
|
|
*/
|
|
if( modeltype==2 )
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][nx] = v->ptr.pp_double[i][nx]+y->ptr.pp_double[j][i];
|
|
}
|
|
if( n>0 )
|
|
{
|
|
v->ptr.pp_double[i][nx] = v->ptr.pp_double[i][nx]/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][nx];
|
|
}
|
|
}
|
|
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<=nx; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Reallocates calcBuf if necessary, reuses previously allocated space if
|
|
possible.
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
static void rbfv2_allocatecalcbuffer(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
if( buf->x.cnt<s->nx )
|
|
{
|
|
ae_vector_set_length(&buf->x, s->nx, _state);
|
|
}
|
|
if( buf->curboxmin.cnt<s->nx )
|
|
{
|
|
ae_vector_set_length(&buf->curboxmin, s->nx, _state);
|
|
}
|
|
if( buf->curboxmax.cnt<s->nx )
|
|
{
|
|
ae_vector_set_length(&buf->curboxmax, s->nx, _state);
|
|
}
|
|
if( buf->x123.cnt<s->nx )
|
|
{
|
|
ae_vector_set_length(&buf->x123, s->nx, _state);
|
|
}
|
|
if( buf->y123.cnt<s->ny )
|
|
{
|
|
ae_vector_set_length(&buf->y123, s->ny, _state);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Extracts structure (and XY-values too) from kd-tree built for a small
|
|
subset of points and appends it to multi-tree.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
static void rbfv2_convertandappendtree(kdtree* curtree,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t nodesbase;
|
|
ae_int_t splitsbase;
|
|
ae_int_t cwbase;
|
|
ae_vector localnodes;
|
|
ae_vector localsplits;
|
|
ae_vector localcw;
|
|
ae_matrix xybuf;
|
|
ae_int_t localnodessize;
|
|
ae_int_t localsplitssize;
|
|
ae_int_t localcwsize;
|
|
ae_int_t i;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&localnodes, 0, sizeof(localnodes));
|
|
memset(&localsplits, 0, sizeof(localsplits));
|
|
memset(&localcw, 0, sizeof(localcw));
|
|
memset(&xybuf, 0, sizeof(xybuf));
|
|
ae_vector_init(&localnodes, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&localsplits, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&localcw, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&xybuf, 0, 0, DT_REAL, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Calculate base offsets
|
|
*/
|
|
nodesbase = kdnodes->cnt;
|
|
splitsbase = kdsplits->cnt;
|
|
cwbase = cw->cnt;
|
|
|
|
/*
|
|
* Prepare local copy of tree
|
|
*/
|
|
ae_vector_set_length(&localnodes, n*rbfv2_maxnodesize, _state);
|
|
ae_vector_set_length(&localsplits, n, _state);
|
|
ae_vector_set_length(&localcw, (nx+ny)*n, _state);
|
|
localnodessize = 0;
|
|
localsplitssize = 0;
|
|
localcwsize = 0;
|
|
rbfv2_converttreerec(curtree, n, nx, ny, 0, nodesbase, splitsbase, cwbase, &localnodes, &localnodessize, &localsplits, &localsplitssize, &localcw, &localcwsize, &xybuf, _state);
|
|
|
|
/*
|
|
* Append to multi-tree
|
|
*/
|
|
ivectorresize(kdnodes, kdnodes->cnt+localnodessize, _state);
|
|
rvectorresize(kdsplits, kdsplits->cnt+localsplitssize, _state);
|
|
rvectorresize(cw, cw->cnt+localcwsize, _state);
|
|
for(i=0; i<=localnodessize-1; i++)
|
|
{
|
|
kdnodes->ptr.p_int[nodesbase+i] = localnodes.ptr.p_int[i];
|
|
}
|
|
for(i=0; i<=localsplitssize-1; i++)
|
|
{
|
|
kdsplits->ptr.p_double[splitsbase+i] = localsplits.ptr.p_double[i];
|
|
}
|
|
for(i=0; i<=localcwsize-1; i++)
|
|
{
|
|
cw->ptr.p_double[cwbase+i] = localcw.ptr.p_double[i];
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Recurrent tree conversion
|
|
|
|
CurTree - tree to convert
|
|
N, NX, NY - dataset metrics
|
|
NodeOffset - offset of current tree node, 0 for root
|
|
NodesBase - a value which is added to intra-tree node indexes;
|
|
although this tree is stored in separate array, it
|
|
is intended to be stored in the larger tree, with
|
|
localNodes being moved to offset NodesBase.
|
|
SplitsBase - similarly, offset of localSplits in the final tree
|
|
CWBase - similarly, offset of localCW in the final tree
|
|
*************************************************************************/
|
|
static void rbfv2_converttreerec(kdtree* curtree,
|
|
ae_int_t n,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t nodeoffset,
|
|
ae_int_t nodesbase,
|
|
ae_int_t splitsbase,
|
|
ae_int_t cwbase,
|
|
/* Integer */ ae_vector* localnodes,
|
|
ae_int_t* localnodessize,
|
|
/* Real */ ae_vector* localsplits,
|
|
ae_int_t* localsplitssize,
|
|
/* Real */ ae_vector* localcw,
|
|
ae_int_t* localcwsize,
|
|
/* Real */ ae_matrix* xybuf,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t nodetype;
|
|
ae_int_t cnt;
|
|
ae_int_t d;
|
|
double s;
|
|
ae_int_t nodele;
|
|
ae_int_t nodege;
|
|
ae_int_t oldnodessize;
|
|
|
|
|
|
kdtreeexplorenodetype(curtree, nodeoffset, &nodetype, _state);
|
|
|
|
/*
|
|
* Leaf node
|
|
*/
|
|
if( nodetype==0 )
|
|
{
|
|
kdtreeexploreleaf(curtree, nodeoffset, xybuf, &cnt, _state);
|
|
ae_assert(localnodes->cnt>=*localnodessize+2, "ConvertTreeRec: integrity check failed", _state);
|
|
ae_assert(localcw->cnt>=*localcwsize+cnt*(nx+ny), "ConvertTreeRec: integrity check failed", _state);
|
|
localnodes->ptr.p_int[*localnodessize+0] = cnt;
|
|
localnodes->ptr.p_int[*localnodessize+1] = cwbase+(*localcwsize);
|
|
*localnodessize = *localnodessize+2;
|
|
for(i=0; i<=cnt-1; i++)
|
|
{
|
|
for(j=0; j<=nx+ny-1; j++)
|
|
{
|
|
localcw->ptr.p_double[*localcwsize+i*(nx+ny)+j] = xybuf->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
*localcwsize = *localcwsize+cnt*(nx+ny);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Split node
|
|
*/
|
|
if( nodetype==1 )
|
|
{
|
|
kdtreeexploresplit(curtree, nodeoffset, &d, &s, &nodele, &nodege, _state);
|
|
ae_assert(localnodes->cnt>=*localnodessize+rbfv2_maxnodesize, "ConvertTreeRec: integrity check failed", _state);
|
|
ae_assert(localsplits->cnt>=*localsplitssize+1, "ConvertTreeRec: integrity check failed", _state);
|
|
oldnodessize = *localnodessize;
|
|
localnodes->ptr.p_int[*localnodessize+0] = 0;
|
|
localnodes->ptr.p_int[*localnodessize+1] = d;
|
|
localnodes->ptr.p_int[*localnodessize+2] = splitsbase+(*localsplitssize);
|
|
localnodes->ptr.p_int[*localnodessize+3] = -1;
|
|
localnodes->ptr.p_int[*localnodessize+4] = -1;
|
|
*localnodessize = *localnodessize+5;
|
|
localsplits->ptr.p_double[*localsplitssize+0] = s;
|
|
*localsplitssize = *localsplitssize+1;
|
|
localnodes->ptr.p_int[oldnodessize+3] = nodesbase+(*localnodessize);
|
|
rbfv2_converttreerec(curtree, n, nx, ny, nodele, nodesbase, splitsbase, cwbase, localnodes, localnodessize, localsplits, localsplitssize, localcw, localcwsize, xybuf, _state);
|
|
localnodes->ptr.p_int[oldnodessize+4] = nodesbase+(*localnodessize);
|
|
rbfv2_converttreerec(curtree, n, nx, ny, nodege, nodesbase, splitsbase, cwbase, localnodes, localnodessize, localsplits, localsplitssize, localcw, localcwsize, xybuf, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Integrity error
|
|
*/
|
|
ae_assert(ae_false, "ConvertTreeRec: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs partial calculation of hierarchical model: given
|
|
evaluation point X and partially computed value Y, it updates Y by values
|
|
computed using part of multi-tree given by RootIdx.
|
|
|
|
INPUT PARAMETERS:
|
|
S - V2 model
|
|
Buf - calc-buffer, this function uses following fields:
|
|
* Buf.CurBoxMin - should be set by caller
|
|
* Buf.CurBoxMax - should be set by caller
|
|
* Buf.CurDist2 - squared distance from X to current bounding box,
|
|
should be set by caller
|
|
RootIdx - offset of partial kd-tree
|
|
InvR2 - 1/R^2, where R is basis function radius
|
|
QueryR2 - squared query radius, usually it is (R*FarRadius(BasisFunction))^2
|
|
X - evaluation point, array[NX]
|
|
Y - partial value, array[NY]
|
|
|
|
OUTPUT PARAMETERS
|
|
Y - updated partial value
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbfv2_partialcalcrec(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double invr2,
|
|
double queryr2,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double ptdist2;
|
|
double v;
|
|
double v0;
|
|
double v1;
|
|
ae_int_t cwoffs;
|
|
ae_int_t cwcnt;
|
|
ae_int_t itemoffs;
|
|
double arg;
|
|
double val;
|
|
ae_int_t d;
|
|
double split;
|
|
ae_int_t childle;
|
|
ae_int_t childge;
|
|
ae_int_t childoffs;
|
|
ae_bool updatemin;
|
|
double prevdist2;
|
|
double t1;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
|
|
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
|
|
/*
|
|
* Helps to avoid spurious warnings
|
|
*/
|
|
val = (double)(0);
|
|
|
|
/*
|
|
* Leaf node.
|
|
*/
|
|
if( s->kdnodes.ptr.p_int[rootidx]>0 )
|
|
{
|
|
cwcnt = s->kdnodes.ptr.p_int[rootidx+0];
|
|
cwoffs = s->kdnodes.ptr.p_int[rootidx+1];
|
|
for(i=0; i<=cwcnt-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate distance
|
|
*/
|
|
itemoffs = cwoffs+i*(nx+ny);
|
|
ptdist2 = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = s->cw.ptr.p_double[itemoffs+j]-x->ptr.p_double[j];
|
|
ptdist2 = ptdist2+v*v;
|
|
}
|
|
|
|
/*
|
|
* Skip points if distance too large
|
|
*/
|
|
if( ptdist2>=queryr2 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Update Y
|
|
*/
|
|
arg = ptdist2*invr2;
|
|
if( s->bf==0 )
|
|
{
|
|
val = ae_exp(-arg, _state);
|
|
}
|
|
else
|
|
{
|
|
if( s->bf==1 )
|
|
{
|
|
val = rbfv2basisfunc(s->bf, arg, _state);
|
|
}
|
|
else
|
|
{
|
|
ae_assert(ae_false, "PartialCalcRec: integrity check failed", _state);
|
|
}
|
|
}
|
|
itemoffs = itemoffs+nx;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
y->ptr.p_double[j] = y->ptr.p_double[j]+val*s->cw.ptr.p_double[itemoffs+j];
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Simple split
|
|
*/
|
|
if( s->kdnodes.ptr.p_int[rootidx]==0 )
|
|
{
|
|
|
|
/*
|
|
* Load:
|
|
* * D dimension to split
|
|
* * Split split position
|
|
* * ChildLE, ChildGE - indexes of childs
|
|
*/
|
|
d = s->kdnodes.ptr.p_int[rootidx+1];
|
|
split = s->kdsplits.ptr.p_double[s->kdnodes.ptr.p_int[rootidx+2]];
|
|
childle = s->kdnodes.ptr.p_int[rootidx+3];
|
|
childge = s->kdnodes.ptr.p_int[rootidx+4];
|
|
|
|
/*
|
|
* Navigate through childs
|
|
*/
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
|
|
/*
|
|
* Select child to process:
|
|
* * ChildOffs current child offset in Nodes[]
|
|
* * UpdateMin whether minimum or maximum value
|
|
* of bounding box is changed on update
|
|
*/
|
|
updatemin = i!=0;
|
|
if( i==0 )
|
|
{
|
|
childoffs = childle;
|
|
}
|
|
else
|
|
{
|
|
childoffs = childge;
|
|
}
|
|
|
|
/*
|
|
* Update bounding box and current distance
|
|
*/
|
|
prevdist2 = buf->curdist2;
|
|
t1 = x->ptr.p_double[d];
|
|
if( updatemin )
|
|
{
|
|
v = buf->curboxmin.ptr.p_double[d];
|
|
if( t1<=split )
|
|
{
|
|
v0 = v-t1;
|
|
if( v0<0 )
|
|
{
|
|
v0 = (double)(0);
|
|
}
|
|
v1 = split-t1;
|
|
buf->curdist2 = buf->curdist2-v0*v0+v1*v1;
|
|
}
|
|
buf->curboxmin.ptr.p_double[d] = split;
|
|
}
|
|
else
|
|
{
|
|
v = buf->curboxmax.ptr.p_double[d];
|
|
if( t1>=split )
|
|
{
|
|
v0 = t1-v;
|
|
if( v0<0 )
|
|
{
|
|
v0 = (double)(0);
|
|
}
|
|
v1 = t1-split;
|
|
buf->curdist2 = buf->curdist2-v0*v0+v1*v1;
|
|
}
|
|
buf->curboxmax.ptr.p_double[d] = split;
|
|
}
|
|
|
|
/*
|
|
* Decide: to dive into cell or not to dive
|
|
*/
|
|
if( buf->curdist2<queryr2 )
|
|
{
|
|
rbfv2_partialcalcrec(s, buf, childoffs, invr2, queryr2, x, y, _state);
|
|
}
|
|
|
|
/*
|
|
* Restore bounding box and distance
|
|
*/
|
|
if( updatemin )
|
|
{
|
|
buf->curboxmin.ptr.p_double[d] = v;
|
|
}
|
|
else
|
|
{
|
|
buf->curboxmax.ptr.p_double[d] = v;
|
|
}
|
|
buf->curdist2 = prevdist2;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Integrity failure
|
|
*/
|
|
ae_assert(ae_false, "PartialCalcRec: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs same operation as partialcalcrec(), but for entire
|
|
row of the grid. "Row" is a set of nodes (x0,x1,x2,x3) which share x1..x3,
|
|
but have different x0's. (note: for 2D/3D problems x2..x3 are zero).
|
|
|
|
Row is given by:
|
|
* central point XC, which is located at the center of the row, and used to
|
|
perform kd-tree requests
|
|
* set of x0 coordinates stored in RX array (array may be unordered, but it
|
|
is expected that spread of x0 is no more than R; function may be
|
|
inefficient for larger spreads).
|
|
* set of YFlag values stored in RF
|
|
|
|
INPUT PARAMETERS:
|
|
S - V2 model
|
|
Buf - calc-buffer, this function uses following fields:
|
|
* Buf.CurBoxMin - should be set by caller
|
|
* Buf.CurBoxMax - should be set by caller
|
|
* Buf.CurDist2 - squared distance from X to current bounding box,
|
|
should be set by caller
|
|
RootIdx - offset of partial kd-tree
|
|
InvR2 - 1/R^2, where R is basis function radius
|
|
RQuery2 - squared query radius, usually it is (R*FarRadius(BasisFunction)+0.5*RowWidth)^2,
|
|
where RowWidth is its spatial extent (after scaling of
|
|
variables). This radius is used to perform initial query
|
|
for neighbors of CX.
|
|
RFar2 - squared far radius; far radius is used to perform actual
|
|
filtering of results of query made with RQuery2.
|
|
CX - central point, array[NX], used for queries
|
|
RX - x0 coordinates, array[RowSize]
|
|
RF - sparsity flags, array[RowSize]
|
|
RowSize - row size in elements
|
|
RY - input partial value, array[NY]
|
|
|
|
OUTPUT PARAMETERS
|
|
RY - updated partial value (function adds its results to RY)
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbfv2_partialrowcalcrec(rbfv2model* s,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double invr2,
|
|
double rquery2,
|
|
double rfar2,
|
|
/* Real */ ae_vector* cx,
|
|
/* Real */ ae_vector* rx,
|
|
/* Boolean */ ae_vector* rf,
|
|
ae_int_t rowsize,
|
|
/* Real */ ae_vector* ry,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
double partialptdist2;
|
|
double ptdist2;
|
|
double v;
|
|
double v0;
|
|
double v1;
|
|
ae_int_t cwoffs;
|
|
ae_int_t cwcnt;
|
|
ae_int_t itemoffs;
|
|
ae_int_t woffs;
|
|
double val;
|
|
ae_int_t d;
|
|
double split;
|
|
ae_int_t childle;
|
|
ae_int_t childge;
|
|
ae_int_t childoffs;
|
|
ae_bool updatemin;
|
|
double prevdist2;
|
|
double t1;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
|
|
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
|
|
/*
|
|
* Leaf node.
|
|
*/
|
|
if( s->kdnodes.ptr.p_int[rootidx]>0 )
|
|
{
|
|
cwcnt = s->kdnodes.ptr.p_int[rootidx+0];
|
|
cwoffs = s->kdnodes.ptr.p_int[rootidx+1];
|
|
for(i0=0; i0<=cwcnt-1; i0++)
|
|
{
|
|
|
|
/*
|
|
* Calculate partial distance (components from 1 to NX-1)
|
|
*/
|
|
itemoffs = cwoffs+i0*(nx+ny);
|
|
partialptdist2 = (double)(0);
|
|
for(j=1; j<=nx-1; j++)
|
|
{
|
|
v = s->cw.ptr.p_double[itemoffs+j]-cx->ptr.p_double[j];
|
|
partialptdist2 = partialptdist2+v*v;
|
|
}
|
|
|
|
/*
|
|
* Process each element of the row
|
|
*/
|
|
for(i1=0; i1<=rowsize-1; i1++)
|
|
{
|
|
if( rf->ptr.p_bool[i1] )
|
|
{
|
|
|
|
/*
|
|
* Calculate distance
|
|
*/
|
|
v = s->cw.ptr.p_double[itemoffs]-rx->ptr.p_double[i1];
|
|
ptdist2 = partialptdist2+v*v;
|
|
|
|
/*
|
|
* Skip points if distance too large
|
|
*/
|
|
if( ptdist2>=rfar2 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Update Y
|
|
*/
|
|
val = rbfv2basisfunc(s->bf, ptdist2*invr2, _state);
|
|
woffs = itemoffs+nx;
|
|
for(j=0; j<=ny-1; j++)
|
|
{
|
|
ry->ptr.p_double[j+i1*ny] = ry->ptr.p_double[j+i1*ny]+val*s->cw.ptr.p_double[woffs+j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Simple split
|
|
*/
|
|
if( s->kdnodes.ptr.p_int[rootidx]==0 )
|
|
{
|
|
|
|
/*
|
|
* Load:
|
|
* * D dimension to split
|
|
* * Split split position
|
|
* * ChildLE, ChildGE - indexes of childs
|
|
*/
|
|
d = s->kdnodes.ptr.p_int[rootidx+1];
|
|
split = s->kdsplits.ptr.p_double[s->kdnodes.ptr.p_int[rootidx+2]];
|
|
childle = s->kdnodes.ptr.p_int[rootidx+3];
|
|
childge = s->kdnodes.ptr.p_int[rootidx+4];
|
|
|
|
/*
|
|
* Navigate through childs
|
|
*/
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
|
|
/*
|
|
* Select child to process:
|
|
* * ChildOffs current child offset in Nodes[]
|
|
* * UpdateMin whether minimum or maximum value
|
|
* of bounding box is changed on update
|
|
*/
|
|
updatemin = i!=0;
|
|
if( i==0 )
|
|
{
|
|
childoffs = childle;
|
|
}
|
|
else
|
|
{
|
|
childoffs = childge;
|
|
}
|
|
|
|
/*
|
|
* Update bounding box and current distance
|
|
*/
|
|
prevdist2 = buf->curdist2;
|
|
t1 = cx->ptr.p_double[d];
|
|
if( updatemin )
|
|
{
|
|
v = buf->curboxmin.ptr.p_double[d];
|
|
if( t1<=split )
|
|
{
|
|
v0 = v-t1;
|
|
if( v0<0 )
|
|
{
|
|
v0 = (double)(0);
|
|
}
|
|
v1 = split-t1;
|
|
buf->curdist2 = buf->curdist2-v0*v0+v1*v1;
|
|
}
|
|
buf->curboxmin.ptr.p_double[d] = split;
|
|
}
|
|
else
|
|
{
|
|
v = buf->curboxmax.ptr.p_double[d];
|
|
if( t1>=split )
|
|
{
|
|
v0 = t1-v;
|
|
if( v0<0 )
|
|
{
|
|
v0 = (double)(0);
|
|
}
|
|
v1 = t1-split;
|
|
buf->curdist2 = buf->curdist2-v0*v0+v1*v1;
|
|
}
|
|
buf->curboxmax.ptr.p_double[d] = split;
|
|
}
|
|
|
|
/*
|
|
* Decide: to dive into cell or not to dive
|
|
*/
|
|
if( buf->curdist2<rquery2 )
|
|
{
|
|
rbfv2_partialrowcalcrec(s, buf, childoffs, invr2, rquery2, rfar2, cx, rx, rf, rowsize, ry, _state);
|
|
}
|
|
|
|
/*
|
|
* Restore bounding box and distance
|
|
*/
|
|
if( updatemin )
|
|
{
|
|
buf->curboxmin.ptr.p_double[d] = v;
|
|
}
|
|
else
|
|
{
|
|
buf->curboxmax.ptr.p_double[d] = v;
|
|
}
|
|
buf->curdist2 = prevdist2;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Integrity failure
|
|
*/
|
|
ae_assert(ae_false, "PartialCalcRec: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function prepares partial query
|
|
|
|
INPUT PARAMETERS:
|
|
X - query point
|
|
kdBoxMin, kdBoxMax - current bounding box
|
|
NX - problem size
|
|
Buf - preallocated buffer; this function just loads data, but
|
|
does not allocate place for them.
|
|
Cnt - counter variable which is set to zery by this function, as
|
|
convenience, and to remember about necessity to zero counter
|
|
prior to calling partialqueryrec().
|
|
|
|
OUTPUT PARAMETERS
|
|
Buf - calc-buffer:
|
|
* Buf.CurBoxMin - current box
|
|
* Buf.CurBoxMax - current box
|
|
* Buf.CurDist2 - squared distance from X to current box
|
|
Cnt - set to zero
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbfv2_preparepartialquery(/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* kdboxmin,
|
|
/* Real */ ae_vector* kdboxmax,
|
|
ae_int_t nx,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t* cnt,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t j;
|
|
|
|
|
|
*cnt = 0;
|
|
buf->curdist2 = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
buf->curboxmin.ptr.p_double[j] = kdboxmin->ptr.p_double[j];
|
|
buf->curboxmax.ptr.p_double[j] = kdboxmax->ptr.p_double[j];
|
|
if( ae_fp_less(x->ptr.p_double[j],buf->curboxmin.ptr.p_double[j]) )
|
|
{
|
|
buf->curdist2 = buf->curdist2+ae_sqr(buf->curboxmin.ptr.p_double[j]-x->ptr.p_double[j], _state);
|
|
}
|
|
else
|
|
{
|
|
if( ae_fp_greater(x->ptr.p_double[j],buf->curboxmax.ptr.p_double[j]) )
|
|
{
|
|
buf->curdist2 = buf->curdist2+ae_sqr(x->ptr.p_double[j]-buf->curboxmax.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs partial (for just one subtree of multi-tree) query
|
|
for neighbors located in R-sphere around X. It returns squared distances
|
|
from X to points and offsets in S.CW[] array for points being found.
|
|
|
|
INPUT PARAMETERS:
|
|
kdNodes, kdSplits, CW, NX, NY - corresponding fields of V2 model
|
|
Buf - calc-buffer, this function uses following fields:
|
|
* Buf.CurBoxMin - should be set by caller
|
|
* Buf.CurBoxMax - should be set by caller
|
|
* Buf.CurDist2 - squared distance from X to current
|
|
bounding box, should be set by caller
|
|
You may use preparepartialquery() function to initialize
|
|
these fields.
|
|
RootIdx - offset of partial kd-tree
|
|
QueryR2 - squared query radius
|
|
X - array[NX], point being queried
|
|
R2 - preallocated output buffer; it is caller's responsibility
|
|
to make sure that R2 has enough space.
|
|
Offs - preallocated output buffer; it is caller's responsibility
|
|
to make sure that Offs has enough space.
|
|
K - MUST BE ZERO ON INITIAL CALL. This variable is incremented,
|
|
not set. So, any no-zero value will result in the incorrect
|
|
points count being returned.
|
|
|
|
OUTPUT PARAMETERS
|
|
R2 - squared distances in first K elements
|
|
Offs - offsets in S.CW in first K elements
|
|
K - points count
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbfv2_partialqueryrec(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double queryr2,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* r2,
|
|
/* Integer */ ae_vector* offs,
|
|
ae_int_t* k,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double ptdist2;
|
|
double v;
|
|
ae_int_t cwoffs;
|
|
ae_int_t cwcnt;
|
|
ae_int_t itemoffs;
|
|
ae_int_t d;
|
|
double split;
|
|
ae_int_t childle;
|
|
ae_int_t childge;
|
|
ae_int_t childoffs;
|
|
ae_bool updatemin;
|
|
double prevdist2;
|
|
double t1;
|
|
|
|
|
|
|
|
/*
|
|
* Leaf node.
|
|
*/
|
|
if( kdnodes->ptr.p_int[rootidx]>0 )
|
|
{
|
|
cwcnt = kdnodes->ptr.p_int[rootidx+0];
|
|
cwoffs = kdnodes->ptr.p_int[rootidx+1];
|
|
for(i=0; i<=cwcnt-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate distance
|
|
*/
|
|
itemoffs = cwoffs+i*(nx+ny);
|
|
ptdist2 = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = cw->ptr.p_double[itemoffs+j]-x->ptr.p_double[j];
|
|
ptdist2 = ptdist2+v*v;
|
|
}
|
|
|
|
/*
|
|
* Skip points if distance too large
|
|
*/
|
|
if( ae_fp_greater_eq(ptdist2,queryr2) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Output
|
|
*/
|
|
r2->ptr.p_double[*k] = ptdist2;
|
|
offs->ptr.p_int[*k] = itemoffs;
|
|
*k = *k+1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Simple split
|
|
*/
|
|
if( kdnodes->ptr.p_int[rootidx]==0 )
|
|
{
|
|
|
|
/*
|
|
* Load:
|
|
* * D dimension to split
|
|
* * Split split position
|
|
* * ChildLE, ChildGE - indexes of childs
|
|
*/
|
|
d = kdnodes->ptr.p_int[rootidx+1];
|
|
split = kdsplits->ptr.p_double[kdnodes->ptr.p_int[rootidx+2]];
|
|
childle = kdnodes->ptr.p_int[rootidx+3];
|
|
childge = kdnodes->ptr.p_int[rootidx+4];
|
|
|
|
/*
|
|
* Navigate through childs
|
|
*/
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
|
|
/*
|
|
* Select child to process:
|
|
* * ChildOffs current child offset in Nodes[]
|
|
* * UpdateMin whether minimum or maximum value
|
|
* of bounding box is changed on update
|
|
*/
|
|
updatemin = i!=0;
|
|
if( i==0 )
|
|
{
|
|
childoffs = childle;
|
|
}
|
|
else
|
|
{
|
|
childoffs = childge;
|
|
}
|
|
|
|
/*
|
|
* Update bounding box and current distance
|
|
*/
|
|
prevdist2 = buf->curdist2;
|
|
t1 = x->ptr.p_double[d];
|
|
if( updatemin )
|
|
{
|
|
v = buf->curboxmin.ptr.p_double[d];
|
|
if( ae_fp_less_eq(t1,split) )
|
|
{
|
|
buf->curdist2 = buf->curdist2-ae_sqr(ae_maxreal(v-t1, (double)(0), _state), _state)+ae_sqr(split-t1, _state);
|
|
}
|
|
buf->curboxmin.ptr.p_double[d] = split;
|
|
}
|
|
else
|
|
{
|
|
v = buf->curboxmax.ptr.p_double[d];
|
|
if( ae_fp_greater_eq(t1,split) )
|
|
{
|
|
buf->curdist2 = buf->curdist2-ae_sqr(ae_maxreal(t1-v, (double)(0), _state), _state)+ae_sqr(t1-split, _state);
|
|
}
|
|
buf->curboxmax.ptr.p_double[d] = split;
|
|
}
|
|
|
|
/*
|
|
* Decide: to dive into cell or not to dive
|
|
*/
|
|
if( ae_fp_less(buf->curdist2,queryr2) )
|
|
{
|
|
rbfv2_partialqueryrec(kdnodes, kdsplits, cw, nx, ny, buf, childoffs, queryr2, x, r2, offs, k, _state);
|
|
}
|
|
|
|
/*
|
|
* Restore bounding box and distance
|
|
*/
|
|
if( updatemin )
|
|
{
|
|
buf->curboxmin.ptr.p_double[d] = v;
|
|
}
|
|
else
|
|
{
|
|
buf->curboxmax.ptr.p_double[d] = v;
|
|
}
|
|
buf->curdist2 = prevdist2;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Integrity failure
|
|
*/
|
|
ae_assert(ae_false, "PartialQueryRec: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs partial (for just one subtree of multi-tree)
|
|
counting of neighbors located in R-sphere around X.
|
|
|
|
This function does not guarantee consistency of results with other partial
|
|
queries, it should be used only to get approximate estimates (well, we do
|
|
not use approximate algorithms, but rounding errors may give us
|
|
inconsistent results in just-at-the-boundary cases).
|
|
|
|
INPUT PARAMETERS:
|
|
kdNodes, kdSplits, CW, NX, NY - corresponding fields of V2 model
|
|
Buf - calc-buffer, this function uses following fields:
|
|
* Buf.CurBoxMin - should be set by caller
|
|
* Buf.CurBoxMax - should be set by caller
|
|
* Buf.CurDist2 - squared distance from X to current
|
|
bounding box, should be set by caller
|
|
You may use preparepartialquery() function to initialize
|
|
these fields.
|
|
RootIdx - offset of partial kd-tree
|
|
QueryR2 - squared query radius
|
|
X - array[NX], point being queried
|
|
|
|
RESULT:
|
|
points count
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static ae_int_t rbfv2_partialcountrec(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv2calcbuffer* buf,
|
|
ae_int_t rootidx,
|
|
double queryr2,
|
|
/* Real */ ae_vector* x,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double ptdist2;
|
|
double v;
|
|
ae_int_t cwoffs;
|
|
ae_int_t cwcnt;
|
|
ae_int_t itemoffs;
|
|
ae_int_t d;
|
|
double split;
|
|
ae_int_t childle;
|
|
ae_int_t childge;
|
|
ae_int_t childoffs;
|
|
ae_bool updatemin;
|
|
double prevdist2;
|
|
double t1;
|
|
ae_int_t result;
|
|
|
|
|
|
result = 0;
|
|
|
|
/*
|
|
* Leaf node.
|
|
*/
|
|
if( kdnodes->ptr.p_int[rootidx]>0 )
|
|
{
|
|
cwcnt = kdnodes->ptr.p_int[rootidx+0];
|
|
cwoffs = kdnodes->ptr.p_int[rootidx+1];
|
|
for(i=0; i<=cwcnt-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate distance
|
|
*/
|
|
itemoffs = cwoffs+i*(nx+ny);
|
|
ptdist2 = (double)(0);
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
v = cw->ptr.p_double[itemoffs+j]-x->ptr.p_double[j];
|
|
ptdist2 = ptdist2+v*v;
|
|
}
|
|
|
|
/*
|
|
* Skip points if distance too large
|
|
*/
|
|
if( ae_fp_greater_eq(ptdist2,queryr2) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Output
|
|
*/
|
|
result = result+1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Simple split
|
|
*/
|
|
if( kdnodes->ptr.p_int[rootidx]==0 )
|
|
{
|
|
|
|
/*
|
|
* Load:
|
|
* * D dimension to split
|
|
* * Split split position
|
|
* * ChildLE, ChildGE - indexes of childs
|
|
*/
|
|
d = kdnodes->ptr.p_int[rootidx+1];
|
|
split = kdsplits->ptr.p_double[kdnodes->ptr.p_int[rootidx+2]];
|
|
childle = kdnodes->ptr.p_int[rootidx+3];
|
|
childge = kdnodes->ptr.p_int[rootidx+4];
|
|
|
|
/*
|
|
* Navigate through childs
|
|
*/
|
|
for(i=0; i<=1; i++)
|
|
{
|
|
|
|
/*
|
|
* Select child to process:
|
|
* * ChildOffs current child offset in Nodes[]
|
|
* * UpdateMin whether minimum or maximum value
|
|
* of bounding box is changed on update
|
|
*/
|
|
updatemin = i!=0;
|
|
if( i==0 )
|
|
{
|
|
childoffs = childle;
|
|
}
|
|
else
|
|
{
|
|
childoffs = childge;
|
|
}
|
|
|
|
/*
|
|
* Update bounding box and current distance
|
|
*/
|
|
prevdist2 = buf->curdist2;
|
|
t1 = x->ptr.p_double[d];
|
|
if( updatemin )
|
|
{
|
|
v = buf->curboxmin.ptr.p_double[d];
|
|
if( ae_fp_less_eq(t1,split) )
|
|
{
|
|
buf->curdist2 = buf->curdist2-ae_sqr(ae_maxreal(v-t1, (double)(0), _state), _state)+ae_sqr(split-t1, _state);
|
|
}
|
|
buf->curboxmin.ptr.p_double[d] = split;
|
|
}
|
|
else
|
|
{
|
|
v = buf->curboxmax.ptr.p_double[d];
|
|
if( ae_fp_greater_eq(t1,split) )
|
|
{
|
|
buf->curdist2 = buf->curdist2-ae_sqr(ae_maxreal(t1-v, (double)(0), _state), _state)+ae_sqr(t1-split, _state);
|
|
}
|
|
buf->curboxmax.ptr.p_double[d] = split;
|
|
}
|
|
|
|
/*
|
|
* Decide: to dive into cell or not to dive
|
|
*/
|
|
if( ae_fp_less(buf->curdist2,queryr2) )
|
|
{
|
|
result = result+rbfv2_partialcountrec(kdnodes, kdsplits, cw, nx, ny, buf, childoffs, queryr2, x, _state);
|
|
}
|
|
|
|
/*
|
|
* Restore bounding box and distance
|
|
*/
|
|
if( updatemin )
|
|
{
|
|
buf->curboxmin.ptr.p_double[d] = v;
|
|
}
|
|
else
|
|
{
|
|
buf->curboxmax.ptr.p_double[d] = v;
|
|
}
|
|
buf->curdist2 = prevdist2;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Integrity failure
|
|
*/
|
|
ae_assert(ae_false, "PartialCountRec: integrity check failed", _state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs partial (for just one subtree of multi-tree) unpack
|
|
for RBF model. It appends center coordinates, weights and per-dimension
|
|
radii (according to current scaling) to preallocated output array.
|
|
|
|
INPUT PARAMETERS:
|
|
kdNodes, kdSplits, CW, S, NX, NY - corresponding fields of V2 model
|
|
RootIdx - offset of partial kd-tree
|
|
R - radius for current partial tree
|
|
XWR - preallocated output buffer; it is caller's responsibility
|
|
to make sure that XWR has enough space. First K rows are
|
|
already occupied.
|
|
K - number of already occupied rows in XWR.
|
|
|
|
OUTPUT PARAMETERS
|
|
XWR - updated XWR
|
|
K - updated rows count
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbfv2_partialunpackrec(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
/* Real */ ae_vector* s,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t rootidx,
|
|
double r,
|
|
/* Real */ ae_matrix* xwr,
|
|
ae_int_t* k,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t childle;
|
|
ae_int_t childge;
|
|
ae_int_t itemoffs;
|
|
ae_int_t cwoffs;
|
|
ae_int_t cwcnt;
|
|
|
|
|
|
|
|
/*
|
|
* Leaf node.
|
|
*/
|
|
if( kdnodes->ptr.p_int[rootidx]>0 )
|
|
{
|
|
cwcnt = kdnodes->ptr.p_int[rootidx+0];
|
|
cwoffs = kdnodes->ptr.p_int[rootidx+1];
|
|
for(i=0; i<=cwcnt-1; i++)
|
|
{
|
|
itemoffs = cwoffs+i*(nx+ny);
|
|
for(j=0; j<=nx+ny-1; j++)
|
|
{
|
|
xwr->ptr.pp_double[*k][j] = cw->ptr.p_double[itemoffs+j];
|
|
}
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
xwr->ptr.pp_double[*k][j] = xwr->ptr.pp_double[*k][j]*s->ptr.p_double[j];
|
|
}
|
|
for(j=0; j<=nx-1; j++)
|
|
{
|
|
xwr->ptr.pp_double[*k][nx+ny+j] = r*s->ptr.p_double[j];
|
|
}
|
|
*k = *k+1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Simple split
|
|
*/
|
|
if( kdnodes->ptr.p_int[rootidx]==0 )
|
|
{
|
|
|
|
/*
|
|
* Load:
|
|
* * ChildLE, ChildGE - indexes of childs
|
|
*/
|
|
childle = kdnodes->ptr.p_int[rootidx+3];
|
|
childge = kdnodes->ptr.p_int[rootidx+4];
|
|
|
|
/*
|
|
* Process both parts of split
|
|
*/
|
|
rbfv2_partialunpackrec(kdnodes, kdsplits, cw, s, nx, ny, childle, r, xwr, k, _state);
|
|
rbfv2_partialunpackrec(kdnodes, kdsplits, cw, s, nx, ny, childge, r, xwr, k, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Integrity failure
|
|
*/
|
|
ae_assert(ae_false, "PartialUnpackRec: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function returns size of design matrix row for evaluation point X0,
|
|
given:
|
|
* query radius multiplier (either RBFV2NearRadius() or RBFV2FarRadius())
|
|
* hierarchy level: value in [0,NH) for single-level model, or negative
|
|
value for multilevel model (all levels of hierarchy in single matrix,
|
|
like one used by nonnegative RBF)
|
|
|
|
INPUT PARAMETERS:
|
|
kdNodes, kdSplits, CW, Ri, kdRoots, kdBoxMin, kdBoxMax, NX, NY, NH - corresponding fields of V2 model
|
|
Level - value in [0,NH) for single-level design matrix, negative
|
|
value for multilevel design matrix
|
|
RCoeff - radius coefficient, either RBFV2NearRadius() or RBFV2FarRadius()
|
|
X0 - query point
|
|
CalcBuf - buffer for PreparePartialQuery(), allocated by caller
|
|
|
|
RESULT:
|
|
row size
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.09.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static ae_int_t rbfv2_designmatrixrowsize(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
/* Real */ ae_vector* ri,
|
|
/* Integer */ ae_vector* kdroots,
|
|
/* Real */ ae_vector* kdboxmin,
|
|
/* Real */ ae_vector* kdboxmax,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t nh,
|
|
ae_int_t level,
|
|
double rcoeff,
|
|
/* Real */ ae_vector* x0,
|
|
rbfv2calcbuffer* calcbuf,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t dummy;
|
|
ae_int_t levelidx;
|
|
ae_int_t level0;
|
|
ae_int_t level1;
|
|
double curradius2;
|
|
ae_int_t result;
|
|
|
|
|
|
ae_assert(nh>0, "DesignMatrixRowSize: integrity failure", _state);
|
|
if( level>=0 )
|
|
{
|
|
level0 = level;
|
|
level1 = level;
|
|
}
|
|
else
|
|
{
|
|
level0 = 0;
|
|
level1 = nh-1;
|
|
}
|
|
result = 0;
|
|
for(levelidx=level0; levelidx<=level1; levelidx++)
|
|
{
|
|
curradius2 = ae_sqr(ri->ptr.p_double[levelidx]*rcoeff, _state);
|
|
rbfv2_preparepartialquery(x0, kdboxmin, kdboxmax, nx, calcbuf, &dummy, _state);
|
|
result = result+rbfv2_partialcountrec(kdnodes, kdsplits, cw, nx, ny, calcbuf, kdroots->ptr.p_int[levelidx], curradius2, x0, _state);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function generates design matrix row for evaluation point X0, given:
|
|
* query radius multiplier (either RBFV2NearRadius() or RBFV2FarRadius())
|
|
* hierarchy level: value in [0,NH) for single-level model, or negative
|
|
value for multilevel model (all levels of hierarchy in single matrix,
|
|
like one used by nonnegative RBF)
|
|
|
|
INPUT PARAMETERS:
|
|
kdNodes, kdSplits, CW, Ri, kdRoots, kdBoxMin, kdBoxMax, NX, NY, NH - corresponding fields of V2 model
|
|
|
|
CWRange - internal array[NH+1] used by RBF construction function,
|
|
stores ranges of CW occupied by NH trees.
|
|
Level - value in [0,NH) for single-level design matrix, negative
|
|
value for multilevel design matrix
|
|
BF - basis function type
|
|
RCoeff - radius coefficient, either RBFV2NearRadius() or RBFV2FarRadius()
|
|
RowsPerPoint-equal to:
|
|
* 1 for unpenalized regression model
|
|
* 1+NX for basic form of nonsmoothness penalty
|
|
Penalty - nonsmoothness penalty coefficient
|
|
|
|
X0 - query point
|
|
|
|
CalcBuf - buffer for PreparePartialQuery(), allocated by caller
|
|
R2 - preallocated temporary buffer, size is at least NPoints;
|
|
it is caller's responsibility to make sure that R2 has enough space.
|
|
Offs - preallocated temporary buffer; size is at least NPoints;
|
|
it is caller's responsibility to make sure that Offs has enough space.
|
|
K - MUST BE ZERO ON INITIAL CALL. This variable is incremented,
|
|
not set. So, any no-zero value will result in the incorrect
|
|
points count being returned.
|
|
RowIdx - preallocated array, at least RowSize elements
|
|
RowVal - preallocated array, at least RowSize*RowsPerPoint elements
|
|
|
|
RESULT:
|
|
RowIdx - RowSize elements are filled with column indexes of non-zero
|
|
design matrix entries
|
|
RowVal - RowSize*RowsPerPoint elements are filled with design matrix
|
|
values, with column RowIdx[0] being stored in first RowsPerPoint
|
|
elements of RowVal, column RowIdx[1] being stored in next
|
|
RowsPerPoint elements, and so on.
|
|
|
|
First element in contiguous set of RowsPerPoint elements
|
|
corresponds to
|
|
|
|
RowSize - number of columns per row
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.09.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbfv2_designmatrixgeneraterow(/* Integer */ ae_vector* kdnodes,
|
|
/* Real */ ae_vector* kdsplits,
|
|
/* Real */ ae_vector* cw,
|
|
/* Real */ ae_vector* ri,
|
|
/* Integer */ ae_vector* kdroots,
|
|
/* Real */ ae_vector* kdboxmin,
|
|
/* Real */ ae_vector* kdboxmax,
|
|
/* Integer */ ae_vector* cwrange,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t nh,
|
|
ae_int_t level,
|
|
ae_int_t bf,
|
|
double rcoeff,
|
|
ae_int_t rowsperpoint,
|
|
double penalty,
|
|
/* Real */ ae_vector* x0,
|
|
rbfv2calcbuffer* calcbuf,
|
|
/* Real */ ae_vector* tmpr2,
|
|
/* Integer */ ae_vector* tmpoffs,
|
|
/* Integer */ ae_vector* rowidx,
|
|
/* Real */ ae_vector* rowval,
|
|
ae_int_t* rowsize,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t cnt;
|
|
ae_int_t levelidx;
|
|
ae_int_t level0;
|
|
ae_int_t level1;
|
|
double invri2;
|
|
double curradius2;
|
|
double val;
|
|
double dval;
|
|
double d2val;
|
|
|
|
*rowsize = 0;
|
|
|
|
ae_assert(nh>0, "DesignMatrixGenerateRow: integrity failure (a)", _state);
|
|
ae_assert(rowsperpoint==1||rowsperpoint==1+nx, "DesignMatrixGenerateRow: integrity failure (b)", _state);
|
|
if( level>=0 )
|
|
{
|
|
level0 = level;
|
|
level1 = level;
|
|
}
|
|
else
|
|
{
|
|
level0 = 0;
|
|
level1 = nh-1;
|
|
}
|
|
*rowsize = 0;
|
|
for(levelidx=level0; levelidx<=level1; levelidx++)
|
|
{
|
|
curradius2 = ae_sqr(ri->ptr.p_double[levelidx]*rcoeff, _state);
|
|
invri2 = 1/ae_sqr(ri->ptr.p_double[levelidx], _state);
|
|
rbfv2_preparepartialquery(x0, kdboxmin, kdboxmax, nx, calcbuf, &cnt, _state);
|
|
rbfv2_partialqueryrec(kdnodes, kdsplits, cw, nx, ny, calcbuf, kdroots->ptr.p_int[levelidx], curradius2, x0, tmpr2, tmpoffs, &cnt, _state);
|
|
ae_assert(tmpr2->cnt>=cnt, "DesignMatrixRowSize: integrity failure (c)", _state);
|
|
ae_assert(tmpoffs->cnt>=cnt, "DesignMatrixRowSize: integrity failure (d)", _state);
|
|
ae_assert(rowidx->cnt>=*rowsize+cnt, "DesignMatrixRowSize: integrity failure (e)", _state);
|
|
ae_assert(rowval->cnt>=rowsperpoint*(*rowsize+cnt), "DesignMatrixRowSize: integrity failure (f)", _state);
|
|
for(j=0; j<=cnt-1; j++)
|
|
{
|
|
|
|
/*
|
|
* Generate element corresponding to fitting error.
|
|
* Store derivative information which may be required later.
|
|
*/
|
|
ae_assert((tmpoffs->ptr.p_int[j]-cwrange->ptr.p_int[level0])%(nx+ny)==0, "DesignMatrixRowSize: integrity failure (g)", _state);
|
|
rbfv2basisfuncdiff2(bf, tmpr2->ptr.p_double[j]*invri2, &val, &dval, &d2val, _state);
|
|
rowidx->ptr.p_int[*rowsize+j] = (tmpoffs->ptr.p_int[j]-cwrange->ptr.p_int[level0])/(nx+ny);
|
|
rowval->ptr.p_double[(*rowsize+j)*rowsperpoint+0] = val;
|
|
if( rowsperpoint==1 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Generate elements corresponding to nonsmoothness penalty
|
|
*/
|
|
ae_assert(rowsperpoint==1+nx, "DesignMatrixRowSize: integrity failure (h)", _state);
|
|
for(k=0; k<=nx-1; k++)
|
|
{
|
|
rowval->ptr.p_double[(*rowsize+j)*rowsperpoint+1+k] = penalty*(dval*2*invri2+d2val*ae_sqr(2*(x0->ptr.p_double[k]-cw->ptr.p_double[tmpoffs->ptr.p_int[j]+k])*invri2, _state));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update columns counter
|
|
*/
|
|
*rowsize = *rowsize+cnt;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function fills RBF model by zeros.
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.11.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbfv2_zerofill(rbfv2model* s,
|
|
ae_int_t nx,
|
|
ae_int_t ny,
|
|
ae_int_t bf,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
|
|
s->bf = bf;
|
|
s->nh = 0;
|
|
ae_vector_set_length(&s->ri, 0, _state);
|
|
ae_vector_set_length(&s->s, 0, _state);
|
|
ae_vector_set_length(&s->kdroots, 0, _state);
|
|
ae_vector_set_length(&s->kdnodes, 0, _state);
|
|
ae_vector_set_length(&s->kdsplits, 0, _state);
|
|
ae_vector_set_length(&s->kdboxmin, 0, _state);
|
|
ae_vector_set_length(&s->kdboxmax, 0, _state);
|
|
ae_vector_set_length(&s->cw, 0, _state);
|
|
ae_matrix_set_length(&s->v, ny, nx+1, _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=nx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void _rbfv2calcbuffer_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2calcbuffer *p = (rbfv2calcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->curboxmin, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->curboxmax, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->x123, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->y123, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv2calcbuffer_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2calcbuffer *dst = (rbfv2calcbuffer*)_dst;
|
|
rbfv2calcbuffer *src = (rbfv2calcbuffer*)_src;
|
|
ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->curboxmin, &src->curboxmin, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->curboxmax, &src->curboxmax, _state, make_automatic);
|
|
dst->curdist2 = src->curdist2;
|
|
ae_vector_init_copy(&dst->x123, &src->x123, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->y123, &src->y123, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv2calcbuffer_clear(void* _p)
|
|
{
|
|
rbfv2calcbuffer *p = (rbfv2calcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->x);
|
|
ae_vector_clear(&p->curboxmin);
|
|
ae_vector_clear(&p->curboxmax);
|
|
ae_vector_clear(&p->x123);
|
|
ae_vector_clear(&p->y123);
|
|
}
|
|
|
|
|
|
void _rbfv2calcbuffer_destroy(void* _p)
|
|
{
|
|
rbfv2calcbuffer *p = (rbfv2calcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->x);
|
|
ae_vector_destroy(&p->curboxmin);
|
|
ae_vector_destroy(&p->curboxmax);
|
|
ae_vector_destroy(&p->x123);
|
|
ae_vector_destroy(&p->y123);
|
|
}
|
|
|
|
|
|
void _rbfv2model_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2model *p = (rbfv2model*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->ri, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->kdroots, 0, DT_INT, _state, make_automatic);
|
|
ae_vector_init(&p->kdnodes, 0, DT_INT, _state, make_automatic);
|
|
ae_vector_init(&p->kdsplits, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->kdboxmin, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->kdboxmax, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->cw, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->v, 0, 0, DT_REAL, _state, make_automatic);
|
|
_rbfv2calcbuffer_init(&p->calcbuf, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv2model_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2model *dst = (rbfv2model*)_dst;
|
|
rbfv2model *src = (rbfv2model*)_src;
|
|
dst->ny = src->ny;
|
|
dst->nx = src->nx;
|
|
dst->bf = src->bf;
|
|
dst->nh = src->nh;
|
|
ae_vector_init_copy(&dst->ri, &src->ri, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->kdroots, &src->kdroots, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->kdnodes, &src->kdnodes, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->kdsplits, &src->kdsplits, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->kdboxmin, &src->kdboxmin, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->kdboxmax, &src->kdboxmax, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->cw, &src->cw, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->v, &src->v, _state, make_automatic);
|
|
dst->lambdareg = src->lambdareg;
|
|
dst->maxits = src->maxits;
|
|
dst->supportr = src->supportr;
|
|
dst->basisfunction = src->basisfunction;
|
|
_rbfv2calcbuffer_init_copy(&dst->calcbuf, &src->calcbuf, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv2model_clear(void* _p)
|
|
{
|
|
rbfv2model *p = (rbfv2model*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->ri);
|
|
ae_vector_clear(&p->s);
|
|
ae_vector_clear(&p->kdroots);
|
|
ae_vector_clear(&p->kdnodes);
|
|
ae_vector_clear(&p->kdsplits);
|
|
ae_vector_clear(&p->kdboxmin);
|
|
ae_vector_clear(&p->kdboxmax);
|
|
ae_vector_clear(&p->cw);
|
|
ae_matrix_clear(&p->v);
|
|
_rbfv2calcbuffer_clear(&p->calcbuf);
|
|
}
|
|
|
|
|
|
void _rbfv2model_destroy(void* _p)
|
|
{
|
|
rbfv2model *p = (rbfv2model*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->ri);
|
|
ae_vector_destroy(&p->s);
|
|
ae_vector_destroy(&p->kdroots);
|
|
ae_vector_destroy(&p->kdnodes);
|
|
ae_vector_destroy(&p->kdsplits);
|
|
ae_vector_destroy(&p->kdboxmin);
|
|
ae_vector_destroy(&p->kdboxmax);
|
|
ae_vector_destroy(&p->cw);
|
|
ae_matrix_destroy(&p->v);
|
|
_rbfv2calcbuffer_destroy(&p->calcbuf);
|
|
}
|
|
|
|
|
|
void _rbfv2gridcalcbuffer_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2gridcalcbuffer *p = (rbfv2gridcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv2calcbuffer_init(&p->calcbuf, _state, make_automatic);
|
|
ae_vector_init(&p->cx, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->rx, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->ry, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tx, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->ty, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->rf, 0, DT_BOOL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv2gridcalcbuffer_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2gridcalcbuffer *dst = (rbfv2gridcalcbuffer*)_dst;
|
|
rbfv2gridcalcbuffer *src = (rbfv2gridcalcbuffer*)_src;
|
|
_rbfv2calcbuffer_init_copy(&dst->calcbuf, &src->calcbuf, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->cx, &src->cx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->rx, &src->rx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->ry, &src->ry, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tx, &src->tx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->ty, &src->ty, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->rf, &src->rf, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv2gridcalcbuffer_clear(void* _p)
|
|
{
|
|
rbfv2gridcalcbuffer *p = (rbfv2gridcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv2calcbuffer_clear(&p->calcbuf);
|
|
ae_vector_clear(&p->cx);
|
|
ae_vector_clear(&p->rx);
|
|
ae_vector_clear(&p->ry);
|
|
ae_vector_clear(&p->tx);
|
|
ae_vector_clear(&p->ty);
|
|
ae_vector_clear(&p->rf);
|
|
}
|
|
|
|
|
|
void _rbfv2gridcalcbuffer_destroy(void* _p)
|
|
{
|
|
rbfv2gridcalcbuffer *p = (rbfv2gridcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv2calcbuffer_destroy(&p->calcbuf);
|
|
ae_vector_destroy(&p->cx);
|
|
ae_vector_destroy(&p->rx);
|
|
ae_vector_destroy(&p->ry);
|
|
ae_vector_destroy(&p->tx);
|
|
ae_vector_destroy(&p->ty);
|
|
ae_vector_destroy(&p->rf);
|
|
}
|
|
|
|
|
|
void _rbfv2report_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2report *p = (rbfv2report*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _rbfv2report_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv2report *dst = (rbfv2report*)_dst;
|
|
rbfv2report *src = (rbfv2report*)_src;
|
|
dst->terminationtype = src->terminationtype;
|
|
dst->maxerror = src->maxerror;
|
|
dst->rmserror = src->rmserror;
|
|
}
|
|
|
|
|
|
void _rbfv2report_clear(void* _p)
|
|
{
|
|
rbfv2report *p = (rbfv2report*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _rbfv2report_destroy(void* _p)
|
|
{
|
|
rbfv2report *p = (rbfv2report*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_SPLINE2D) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates the value of the bilinear or bicubic spline at
|
|
the given point X.
|
|
|
|
Input parameters:
|
|
C - 2D spline object.
|
|
Built by spline2dbuildbilinearv or spline2dbuildbicubicv.
|
|
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)
|
|
{
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t h;
|
|
double t;
|
|
double dt;
|
|
double u;
|
|
double du;
|
|
double y1;
|
|
double y2;
|
|
double y3;
|
|
double y4;
|
|
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 t2;
|
|
double t3;
|
|
double u2;
|
|
double u3;
|
|
double ht00;
|
|
double ht01;
|
|
double ht10;
|
|
double ht11;
|
|
double hu00;
|
|
double hu01;
|
|
double hu10;
|
|
double hu11;
|
|
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 = (double)(0);
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Determine evaluation interval
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
dt = 1.0/(c->x.ptr.p_double[l+1]-c->x.ptr.p_double[l]);
|
|
t = (x-c->x.ptr.p_double[l])*dt;
|
|
ix = l;
|
|
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;
|
|
}
|
|
}
|
|
du = 1.0/(c->y.ptr.p_double[l+1]-c->y.ptr.p_double[l]);
|
|
u = (y-c->y.ptr.p_double[l])*du;
|
|
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];
|
|
result = (1-t)*(1-u)*y1+t*(1-u)*y2+t*u*y3+(1-t)*u*y4;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Bicubic interpolation:
|
|
* * calculate Hermite basis for dimensions X and Y (variables T and U),
|
|
* here HTij means basis function whose I-th derivative has value 1 at T=J.
|
|
* Same for HUij.
|
|
* * after initial calculation, apply scaling by DT/DU to the basis
|
|
* * calculate using stored table of second derivatives
|
|
*/
|
|
ae_assert(c->stype==-3, "Spline2DCalc: integrity check failed", _state);
|
|
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;
|
|
s4 = c->n*(iy+1)+(ix+1);
|
|
t2 = t*t;
|
|
t3 = t*t2;
|
|
u2 = u*u;
|
|
u3 = u*u2;
|
|
ht00 = 2*t3-3*t2+1;
|
|
ht10 = t3-2*t2+t;
|
|
ht01 = -2*t3+3*t2;
|
|
ht11 = t3-t2;
|
|
hu00 = 2*u3-3*u2+1;
|
|
hu10 = u3-2*u2+u;
|
|
hu01 = -2*u3+3*u2;
|
|
hu11 = u3-u2;
|
|
ht10 = ht10/dt;
|
|
ht11 = ht11/dt;
|
|
hu10 = hu10/du;
|
|
hu11 = hu11/du;
|
|
result = (double)(0);
|
|
result = result+c->f.ptr.p_double[s1]*ht00*hu00+c->f.ptr.p_double[s2]*ht01*hu00+c->f.ptr.p_double[s3]*ht00*hu01+c->f.ptr.p_double[s4]*ht01*hu01;
|
|
result = result+c->f.ptr.p_double[sfx+s1]*ht10*hu00+c->f.ptr.p_double[sfx+s2]*ht11*hu00+c->f.ptr.p_double[sfx+s3]*ht10*hu01+c->f.ptr.p_double[sfx+s4]*ht11*hu01;
|
|
result = result+c->f.ptr.p_double[sfy+s1]*ht00*hu10+c->f.ptr.p_double[sfy+s2]*ht01*hu10+c->f.ptr.p_double[sfy+s3]*ht00*hu11+c->f.ptr.p_double[sfy+s4]*ht01*hu11;
|
|
result = result+c->f.ptr.p_double[sfxy+s1]*ht10*hu10+c->f.ptr.p_double[sfxy+s2]*ht11*hu10+c->f.ptr.p_double[sfxy+s3]*ht10*hu11+c->f.ptr.p_double[sfxy+s4]*ht11*hu11;
|
|
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 v0;
|
|
double v1;
|
|
double v2;
|
|
double v3;
|
|
double t2;
|
|
double t3;
|
|
double u2;
|
|
double u3;
|
|
double ht00;
|
|
double ht01;
|
|
double ht10;
|
|
double ht11;
|
|
double hu00;
|
|
double hu01;
|
|
double hu10;
|
|
double hu11;
|
|
double dht00;
|
|
double dht01;
|
|
double dht10;
|
|
double dht11;
|
|
double dhu00;
|
|
double dhu01;
|
|
double dhu10;
|
|
double dhu11;
|
|
|
|
*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 = (double)(0);
|
|
*fx = (double)(0);
|
|
*fy = (double)(0);
|
|
*fxy = (double)(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 )
|
|
{
|
|
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;
|
|
s4 = c->n*(iy+1)+(ix+1);
|
|
t2 = t*t;
|
|
t3 = t*t2;
|
|
u2 = u*u;
|
|
u3 = u*u2;
|
|
ht00 = 2*t3-3*t2+1;
|
|
ht10 = t3-2*t2+t;
|
|
ht01 = -2*t3+3*t2;
|
|
ht11 = t3-t2;
|
|
hu00 = 2*u3-3*u2+1;
|
|
hu10 = u3-2*u2+u;
|
|
hu01 = -2*u3+3*u2;
|
|
hu11 = u3-u2;
|
|
ht10 = ht10/dt;
|
|
ht11 = ht11/dt;
|
|
hu10 = hu10/du;
|
|
hu11 = hu11/du;
|
|
dht00 = 6*t2-6*t;
|
|
dht10 = 3*t2-4*t+1;
|
|
dht01 = -6*t2+6*t;
|
|
dht11 = 3*t2-2*t;
|
|
dhu00 = 6*u2-6*u;
|
|
dhu10 = 3*u2-4*u+1;
|
|
dhu01 = -6*u2+6*u;
|
|
dhu11 = 3*u2-2*u;
|
|
dht00 = dht00*dt;
|
|
dht01 = dht01*dt;
|
|
dhu00 = dhu00*du;
|
|
dhu01 = dhu01*du;
|
|
*f = (double)(0);
|
|
*fx = (double)(0);
|
|
*fy = (double)(0);
|
|
*fxy = (double)(0);
|
|
v0 = c->f.ptr.p_double[s1];
|
|
v1 = c->f.ptr.p_double[s2];
|
|
v2 = c->f.ptr.p_double[s3];
|
|
v3 = c->f.ptr.p_double[s4];
|
|
*f = *f+v0*ht00*hu00+v1*ht01*hu00+v2*ht00*hu01+v3*ht01*hu01;
|
|
*fx = *fx+v0*dht00*hu00+v1*dht01*hu00+v2*dht00*hu01+v3*dht01*hu01;
|
|
*fy = *fy+v0*ht00*dhu00+v1*ht01*dhu00+v2*ht00*dhu01+v3*ht01*dhu01;
|
|
*fxy = *fxy+v0*dht00*dhu00+v1*dht01*dhu00+v2*dht00*dhu01+v3*dht01*dhu01;
|
|
v0 = c->f.ptr.p_double[sfx+s1];
|
|
v1 = c->f.ptr.p_double[sfx+s2];
|
|
v2 = c->f.ptr.p_double[sfx+s3];
|
|
v3 = c->f.ptr.p_double[sfx+s4];
|
|
*f = *f+v0*ht10*hu00+v1*ht11*hu00+v2*ht10*hu01+v3*ht11*hu01;
|
|
*fx = *fx+v0*dht10*hu00+v1*dht11*hu00+v2*dht10*hu01+v3*dht11*hu01;
|
|
*fy = *fy+v0*ht10*dhu00+v1*ht11*dhu00+v2*ht10*dhu01+v3*ht11*dhu01;
|
|
*fxy = *fxy+v0*dht10*dhu00+v1*dht11*dhu00+v2*dht10*dhu01+v3*dht11*dhu01;
|
|
v0 = c->f.ptr.p_double[sfy+s1];
|
|
v1 = c->f.ptr.p_double[sfy+s2];
|
|
v2 = c->f.ptr.p_double[sfy+s3];
|
|
v3 = c->f.ptr.p_double[sfy+s4];
|
|
*f = *f+v0*ht00*hu10+v1*ht01*hu10+v2*ht00*hu11+v3*ht01*hu11;
|
|
*fx = *fx+v0*dht00*hu10+v1*dht01*hu10+v2*dht00*hu11+v3*dht01*hu11;
|
|
*fy = *fy+v0*ht00*dhu10+v1*ht01*dhu10+v2*ht00*dhu11+v3*ht01*dhu11;
|
|
*fxy = *fxy+v0*dht00*dhu10+v1*dht01*dhu10+v2*dht00*dhu11+v3*dht01*dhu11;
|
|
v0 = c->f.ptr.p_double[sfxy+s1];
|
|
v1 = c->f.ptr.p_double[sfxy+s2];
|
|
v2 = c->f.ptr.p_double[sfxy+s3];
|
|
v3 = c->f.ptr.p_double[sfxy+s4];
|
|
*f = *f+v0*ht10*hu10+v1*ht11*hu10+v2*ht10*hu11+v3*ht11*hu11;
|
|
*fx = *fx+v0*dht10*hu10+v1*dht11*hu10+v2*dht10*hu11+v3*dht11*hu11;
|
|
*fy = *fy+v0*ht10*dhu10+v1*ht11*dhu10+v2*ht10*dhu11+v3*ht11*dhu11;
|
|
*fxy = *fxy+v0*dht10*dhu10+v1*dht11*dhu10+v2*dht10*dhu11+v3*dht11*dhu11;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates bilinear or bicubic vector-valued spline at the
|
|
given point (X,Y).
|
|
|
|
If you need just some specific component of vector-valued spline, you can
|
|
use spline2dcalcvi() function.
|
|
|
|
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 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dcalcvbuf(spline2dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
/* Real */ ae_vector* f,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t h;
|
|
ae_int_t i;
|
|
double t;
|
|
double dt;
|
|
double u;
|
|
double du;
|
|
double y1;
|
|
double y2;
|
|
double y3;
|
|
double y4;
|
|
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 t2;
|
|
double t3;
|
|
double u2;
|
|
double u3;
|
|
double ht00;
|
|
double ht01;
|
|
double ht10;
|
|
double ht11;
|
|
double hu00;
|
|
double hu01;
|
|
double hu10;
|
|
double hu11;
|
|
|
|
|
|
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: X or Y contains NaN or Infinite value", _state);
|
|
|
|
/*
|
|
* Allocate place for output
|
|
*/
|
|
rvectorsetlengthatleast(f, c->d, _state);
|
|
|
|
/*
|
|
* Determine evaluation interval
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
dt = 1.0/(c->x.ptr.p_double[l+1]-c->x.ptr.p_double[l]);
|
|
t = (x-c->x.ptr.p_double[l])*dt;
|
|
ix = l;
|
|
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;
|
|
}
|
|
}
|
|
du = 1.0/(c->y.ptr.p_double[l+1]-c->y.ptr.p_double[l]);
|
|
u = (y-c->y.ptr.p_double[l])*du;
|
|
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:
|
|
* * calculate Hermite basis for dimensions X and Y (variables T and U),
|
|
* here HTij means basis function whose I-th derivative has value 1 at T=J.
|
|
* Same for HUij.
|
|
* * after initial calculation, apply scaling by DT/DU to the basis
|
|
* * calculate using stored table of second derivatives
|
|
*/
|
|
ae_assert(c->stype==-3, "Spline2DCalc: integrity check failed", _state);
|
|
sfx = c->n*c->m*c->d;
|
|
sfy = 2*c->n*c->m*c->d;
|
|
sfxy = 3*c->n*c->m*c->d;
|
|
s1 = (c->n*iy+ix)*c->d;
|
|
s2 = (c->n*iy+(ix+1))*c->d;
|
|
s3 = (c->n*(iy+1)+ix)*c->d;
|
|
s4 = (c->n*(iy+1)+(ix+1))*c->d;
|
|
t2 = t*t;
|
|
t3 = t*t2;
|
|
u2 = u*u;
|
|
u3 = u*u2;
|
|
ht00 = 2*t3-3*t2+1;
|
|
ht10 = t3-2*t2+t;
|
|
ht01 = -2*t3+3*t2;
|
|
ht11 = t3-t2;
|
|
hu00 = 2*u3-3*u2+1;
|
|
hu10 = u3-2*u2+u;
|
|
hu01 = -2*u3+3*u2;
|
|
hu11 = u3-u2;
|
|
ht10 = ht10/dt;
|
|
ht11 = ht11/dt;
|
|
hu10 = hu10/du;
|
|
hu11 = hu11/du;
|
|
for(i=0; i<=c->d-1; i++)
|
|
{
|
|
|
|
/*
|
|
* Calculate I-th component
|
|
*/
|
|
f->ptr.p_double[i] = (double)(0);
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+c->f.ptr.p_double[s1]*ht00*hu00+c->f.ptr.p_double[s2]*ht01*hu00+c->f.ptr.p_double[s3]*ht00*hu01+c->f.ptr.p_double[s4]*ht01*hu01;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+c->f.ptr.p_double[sfx+s1]*ht10*hu00+c->f.ptr.p_double[sfx+s2]*ht11*hu00+c->f.ptr.p_double[sfx+s3]*ht10*hu01+c->f.ptr.p_double[sfx+s4]*ht11*hu01;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+c->f.ptr.p_double[sfy+s1]*ht00*hu10+c->f.ptr.p_double[sfy+s2]*ht01*hu10+c->f.ptr.p_double[sfy+s3]*ht00*hu11+c->f.ptr.p_double[sfy+s4]*ht01*hu11;
|
|
f->ptr.p_double[i] = f->ptr.p_double[i]+c->f.ptr.p_double[sfxy+s1]*ht10*hu10+c->f.ptr.p_double[sfxy+s2]*ht11*hu10+c->f.ptr.p_double[sfxy+s3]*ht10*hu11+c->f.ptr.p_double[sfxy+s4]*ht11*hu11;
|
|
|
|
/*
|
|
* Advance source indexes
|
|
*/
|
|
s1 = s1+1;
|
|
s2 = s2+1;
|
|
s3 = s3+1;
|
|
s4 = s4+1;
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates specific component of vector-valued bilinear or
|
|
bicubic spline at the given point (X,Y).
|
|
|
|
INPUT PARAMETERS:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
I - component index, in [0,D). An exception is generated for out
|
|
of range values.
|
|
|
|
RESULT:
|
|
value of I-th component
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double spline2dcalcvi(spline2dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
ae_int_t i,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t ix;
|
|
ae_int_t iy;
|
|
ae_int_t l;
|
|
ae_int_t r;
|
|
ae_int_t h;
|
|
double t;
|
|
double dt;
|
|
double u;
|
|
double du;
|
|
double y1;
|
|
double y2;
|
|
double y3;
|
|
double y4;
|
|
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 t2;
|
|
double t3;
|
|
double u2;
|
|
double u3;
|
|
double ht00;
|
|
double ht01;
|
|
double ht10;
|
|
double ht11;
|
|
double hu00;
|
|
double hu01;
|
|
double hu10;
|
|
double hu11;
|
|
double result;
|
|
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline2DCalcVi: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(x, _state)&&ae_isfinite(y, _state), "Spline2DCalcVi: X or Y contains NaN or Infinite value", _state);
|
|
ae_assert(i>=0&&i<c->d, "Spline2DCalcVi: incorrect I (I<0 or I>=D)", _state);
|
|
|
|
/*
|
|
* Determine evaluation interval
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
dt = 1.0/(c->x.ptr.p_double[l+1]-c->x.ptr.p_double[l]);
|
|
t = (x-c->x.ptr.p_double[l])*dt;
|
|
ix = l;
|
|
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;
|
|
}
|
|
}
|
|
du = 1.0/(c->y.ptr.p_double[l+1]-c->y.ptr.p_double[l]);
|
|
u = (y-c->y.ptr.p_double[l])*du;
|
|
iy = l;
|
|
|
|
/*
|
|
* Bilinear interpolation
|
|
*/
|
|
if( c->stype==-1 )
|
|
{
|
|
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];
|
|
result = (1-t)*(1-u)*y1+t*(1-u)*y2+t*u*y3+(1-t)*u*y4;
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* Bicubic interpolation:
|
|
* * calculate Hermite basis for dimensions X and Y (variables T and U),
|
|
* here HTij means basis function whose I-th derivative has value 1 at T=J.
|
|
* Same for HUij.
|
|
* * after initial calculation, apply scaling by DT/DU to the basis
|
|
* * calculate using stored table of second derivatives
|
|
*/
|
|
ae_assert(c->stype==-3, "Spline2DCalc: integrity check failed", _state);
|
|
sfx = c->n*c->m*c->d;
|
|
sfy = 2*c->n*c->m*c->d;
|
|
sfxy = 3*c->n*c->m*c->d;
|
|
s1 = (c->n*iy+ix)*c->d;
|
|
s2 = (c->n*iy+(ix+1))*c->d;
|
|
s3 = (c->n*(iy+1)+ix)*c->d;
|
|
s4 = (c->n*(iy+1)+(ix+1))*c->d;
|
|
t2 = t*t;
|
|
t3 = t*t2;
|
|
u2 = u*u;
|
|
u3 = u*u2;
|
|
ht00 = 2*t3-3*t2+1;
|
|
ht10 = t3-2*t2+t;
|
|
ht01 = -2*t3+3*t2;
|
|
ht11 = t3-t2;
|
|
hu00 = 2*u3-3*u2+1;
|
|
hu10 = u3-2*u2+u;
|
|
hu01 = -2*u3+3*u2;
|
|
hu11 = u3-u2;
|
|
ht10 = ht10/dt;
|
|
ht11 = ht11/dt;
|
|
hu10 = hu10/du;
|
|
hu11 = hu11/du;
|
|
|
|
/*
|
|
* Advance source indexes to I-th position
|
|
*/
|
|
s1 = s1+i;
|
|
s2 = s2+i;
|
|
s3 = s3+i;
|
|
s4 = s4+i;
|
|
|
|
/*
|
|
* Calculate I-th component
|
|
*/
|
|
result = (double)(0);
|
|
result = result+c->f.ptr.p_double[s1]*ht00*hu00+c->f.ptr.p_double[s2]*ht01*hu00+c->f.ptr.p_double[s3]*ht00*hu01+c->f.ptr.p_double[s4]*ht01*hu01;
|
|
result = result+c->f.ptr.p_double[sfx+s1]*ht10*hu00+c->f.ptr.p_double[sfx+s2]*ht11*hu00+c->f.ptr.p_double[sfx+s3]*ht10*hu01+c->f.ptr.p_double[sfx+s4]*ht11*hu01;
|
|
result = result+c->f.ptr.p_double[sfy+s1]*ht00*hu10+c->f.ptr.p_double[sfy+s2]*ht01*hu10+c->f.ptr.p_double[sfy+s3]*ht00*hu11+c->f.ptr.p_double[sfy+s4]*ht01*hu11;
|
|
result = result+c->f.ptr.p_double[sfxy+s1]*ht10*hu10+c->f.ptr.p_double[sfxy+s2]*ht11*hu10+c->f.ptr.p_double[sfxy+s3]*ht10*hu11+c->f.ptr.p_double[sfxy+s4]*ht11*hu11;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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);
|
|
spline2dcalcvbuf(c, x, y, f, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine calculates value of specific component of bilinear or
|
|
bicubic vector-valued spline and its derivatives.
|
|
|
|
Input parameters:
|
|
C - spline interpolant.
|
|
X, Y- point
|
|
I - component index, in [0,D)
|
|
|
|
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 spline2ddiffvi(spline2dinterpolant* c,
|
|
double x,
|
|
double y,
|
|
ae_int_t i,
|
|
double* f,
|
|
double* fx,
|
|
double* fy,
|
|
double* fxy,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t d;
|
|
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 v0;
|
|
double v1;
|
|
double v2;
|
|
double v3;
|
|
double t2;
|
|
double t3;
|
|
double u2;
|
|
double u3;
|
|
double ht00;
|
|
double ht01;
|
|
double ht10;
|
|
double ht11;
|
|
double hu00;
|
|
double hu01;
|
|
double hu10;
|
|
double hu11;
|
|
double dht00;
|
|
double dht01;
|
|
double dht10;
|
|
double dht11;
|
|
double dhu00;
|
|
double dhu01;
|
|
double dhu10;
|
|
double dhu11;
|
|
|
|
*f = 0;
|
|
*fx = 0;
|
|
*fy = 0;
|
|
*fxy = 0;
|
|
|
|
ae_assert(c->stype==-1||c->stype==-3, "Spline2DDiffVI: incorrect C (incorrect parameter C.SType)", _state);
|
|
ae_assert(ae_isfinite(x, _state)&&ae_isfinite(y, _state), "Spline2DDiffVI: X or Y contains NaN or Infinite value", _state);
|
|
ae_assert(i>=0&&i<c->d, "Spline2DDiffVI: I<0 or I>=D", _state);
|
|
|
|
/*
|
|
* Prepare F, dF/dX, dF/dY, d2F/dXdY
|
|
*/
|
|
*f = (double)(0);
|
|
*fx = (double)(0);
|
|
*fy = (double)(0);
|
|
*fxy = (double)(0);
|
|
d = c->d;
|
|
|
|
/*
|
|
* 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[d*(c->n*iy+ix)+i];
|
|
y2 = c->f.ptr.p_double[d*(c->n*iy+(ix+1))+i];
|
|
y3 = c->f.ptr.p_double[d*(c->n*(iy+1)+(ix+1))+i];
|
|
y4 = c->f.ptr.p_double[d*(c->n*(iy+1)+ix)+i];
|
|
*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 )
|
|
{
|
|
sfx = c->n*c->m*d;
|
|
sfy = 2*c->n*c->m*d;
|
|
sfxy = 3*c->n*c->m*d;
|
|
s1 = d*(c->n*iy+ix)+i;
|
|
s2 = d*(c->n*iy+(ix+1))+i;
|
|
s3 = d*(c->n*(iy+1)+ix)+i;
|
|
s4 = d*(c->n*(iy+1)+(ix+1))+i;
|
|
t2 = t*t;
|
|
t3 = t*t2;
|
|
u2 = u*u;
|
|
u3 = u*u2;
|
|
ht00 = 2*t3-3*t2+1;
|
|
ht10 = t3-2*t2+t;
|
|
ht01 = -2*t3+3*t2;
|
|
ht11 = t3-t2;
|
|
hu00 = 2*u3-3*u2+1;
|
|
hu10 = u3-2*u2+u;
|
|
hu01 = -2*u3+3*u2;
|
|
hu11 = u3-u2;
|
|
ht10 = ht10/dt;
|
|
ht11 = ht11/dt;
|
|
hu10 = hu10/du;
|
|
hu11 = hu11/du;
|
|
dht00 = 6*t2-6*t;
|
|
dht10 = 3*t2-4*t+1;
|
|
dht01 = -6*t2+6*t;
|
|
dht11 = 3*t2-2*t;
|
|
dhu00 = 6*u2-6*u;
|
|
dhu10 = 3*u2-4*u+1;
|
|
dhu01 = -6*u2+6*u;
|
|
dhu11 = 3*u2-2*u;
|
|
dht00 = dht00*dt;
|
|
dht01 = dht01*dt;
|
|
dhu00 = dhu00*du;
|
|
dhu01 = dhu01*du;
|
|
*f = (double)(0);
|
|
*fx = (double)(0);
|
|
*fy = (double)(0);
|
|
*fxy = (double)(0);
|
|
v0 = c->f.ptr.p_double[s1];
|
|
v1 = c->f.ptr.p_double[s2];
|
|
v2 = c->f.ptr.p_double[s3];
|
|
v3 = c->f.ptr.p_double[s4];
|
|
*f = *f+v0*ht00*hu00+v1*ht01*hu00+v2*ht00*hu01+v3*ht01*hu01;
|
|
*fx = *fx+v0*dht00*hu00+v1*dht01*hu00+v2*dht00*hu01+v3*dht01*hu01;
|
|
*fy = *fy+v0*ht00*dhu00+v1*ht01*dhu00+v2*ht00*dhu01+v3*ht01*dhu01;
|
|
*fxy = *fxy+v0*dht00*dhu00+v1*dht01*dhu00+v2*dht00*dhu01+v3*dht01*dhu01;
|
|
v0 = c->f.ptr.p_double[sfx+s1];
|
|
v1 = c->f.ptr.p_double[sfx+s2];
|
|
v2 = c->f.ptr.p_double[sfx+s3];
|
|
v3 = c->f.ptr.p_double[sfx+s4];
|
|
*f = *f+v0*ht10*hu00+v1*ht11*hu00+v2*ht10*hu01+v3*ht11*hu01;
|
|
*fx = *fx+v0*dht10*hu00+v1*dht11*hu00+v2*dht10*hu01+v3*dht11*hu01;
|
|
*fy = *fy+v0*ht10*dhu00+v1*ht11*dhu00+v2*ht10*dhu01+v3*ht11*dhu01;
|
|
*fxy = *fxy+v0*dht10*dhu00+v1*dht11*dhu00+v2*dht10*dhu01+v3*dht11*dhu01;
|
|
v0 = c->f.ptr.p_double[sfy+s1];
|
|
v1 = c->f.ptr.p_double[sfy+s2];
|
|
v2 = c->f.ptr.p_double[sfy+s3];
|
|
v3 = c->f.ptr.p_double[sfy+s4];
|
|
*f = *f+v0*ht00*hu10+v1*ht01*hu10+v2*ht00*hu11+v3*ht01*hu11;
|
|
*fx = *fx+v0*dht00*hu10+v1*dht01*hu10+v2*dht00*hu11+v3*dht01*hu11;
|
|
*fy = *fy+v0*ht00*dhu10+v1*ht01*dhu10+v2*ht00*dhu11+v3*ht01*dhu11;
|
|
*fxy = *fxy+v0*dht00*dhu10+v1*dht01*dhu10+v2*dht00*dhu11+v3*dht01*dhu11;
|
|
v0 = c->f.ptr.p_double[sfxy+s1];
|
|
v1 = c->f.ptr.p_double[sfxy+s2];
|
|
v2 = c->f.ptr.p_double[sfxy+s3];
|
|
v3 = c->f.ptr.p_double[sfxy+s4];
|
|
*f = *f+v0*ht10*hu10+v1*ht11*hu10+v2*ht10*hu11+v3*ht11*hu11;
|
|
*fx = *fx+v0*dht10*hu10+v1*dht11*hu10+v2*dht10*hu11+v3*dht11*hu11;
|
|
*fy = *fy+v0*ht10*dhu10+v1*ht11*dhu10+v2*ht10*dhu11+v3*ht11*dhu11;
|
|
*fxy = *fxy+v0*dht10*dhu10+v1*dht11*dhu10+v2*dht10*dhu11+v3*dht11*dhu11;
|
|
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);
|
|
memset(&x, 0, sizeof(x));
|
|
memset(&y, 0, sizeof(y));
|
|
memset(&f, 0, sizeof(f));
|
|
memset(&v, 0, sizeof(v));
|
|
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,(double)(0))&&ae_fp_neq(ay,(double)(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,(double)(0))&&ae_fp_eq(ay,(double)(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,(double)(0))&&ae_fp_neq(ay,(double)(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,(double)(0))&&ae_fp_eq(ay,(double)(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);
|
|
memset(&x, 0, sizeof(x));
|
|
memset(&y, 0, sizeof(y));
|
|
memset(&f, 0, sizeof(f));
|
|
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->stype==-1||c->stype==-3, "Spline2DCopy: incorrect C (incorrect parameter C.SType)", _state);
|
|
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);
|
|
memset(&buf, 0, sizeof(buf));
|
|
memset(&x, 0, sizeof(x));
|
|
memset(&y, 0, sizeof(y));
|
|
memset(&c, 0, sizeof(c));
|
|
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->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);
|
|
memset(&_f, 0, sizeof(_f));
|
|
memset(&tf, 0, sizeof(tf));
|
|
memset(&dx, 0, sizeof(dx));
|
|
memset(&dy, 0, sizeof(dy));
|
|
memset(&dxy, 0, sizeof(dxy));
|
|
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->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 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] = (double)(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, (double)(ci), _state)*ae_pow(du, (double)(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->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);
|
|
memset(&_f, 0, sizeof(_f));
|
|
memset(&dx, 0, sizeof(dx));
|
|
memset(&dy, 0, sizeof(dy));
|
|
memset(&dxy, 0, sizeof(dxy));
|
|
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->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] = (double)(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, (double)(ci), _state)*ae_pow(du, (double)(cj), _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This subroutine creates least squares solver used to fit 2D splines to
|
|
irregularly sampled (scattered) data.
|
|
|
|
Solver object is used to perform spline fits as follows:
|
|
* solver object is created with spline2dbuildercreate() function
|
|
* dataset is added with spline2dbuildersetpoints() function
|
|
* fit area is chosen:
|
|
* spline2dbuildersetarea() - for user-defined area
|
|
* spline2dbuildersetareaauto() - for automatically chosen area
|
|
* number of grid nodes is chosen with spline2dbuildersetgrid()
|
|
* prior term is chosen with one of the following functions:
|
|
* spline2dbuildersetlinterm() to set linear prior
|
|
* spline2dbuildersetconstterm() to set constant prior
|
|
* spline2dbuildersetzeroterm() to set zero prior
|
|
* spline2dbuildersetuserterm() to set user-defined constant prior
|
|
* solver algorithm is chosen with either:
|
|
* spline2dbuildersetalgoblocklls() - BlockLLS algorithm, medium-scale problems
|
|
* spline2dbuildersetalgofastddm() - FastDDM algorithm, large-scale problems
|
|
* finally, fitting itself is performed with spline2dfit() function.
|
|
|
|
Most of the steps above can be omitted, solver is configured with good
|
|
defaults. The minimum is to call:
|
|
* spline2dbuildercreate() to create solver object
|
|
* spline2dbuildersetpoints() to specify dataset
|
|
* spline2dbuildersetgrid() to tell how many nodes you need
|
|
* spline2dfit() to perform fit
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
D - positive number, number of Y-components: D=1 for simple scalar
|
|
fit, D>1 for vector-valued spline fitting.
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - solver object
|
|
|
|
-- ALGLIB PROJECT --
|
|
Copyright 29.01.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildercreate(ae_int_t d,
|
|
spline2dbuilder* state,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_spline2dbuilder_clear(state);
|
|
|
|
ae_assert(d>=1, "Spline2DBuilderCreate: D<=0", _state);
|
|
|
|
/*
|
|
* NOTES:
|
|
*
|
|
* 1. Prior term is set to linear one (good default option)
|
|
* 2. Solver is set to BlockLLS - good enough for small-scale problems.
|
|
* 3. Refinement rounds: 5; enough to get good convergence.
|
|
*/
|
|
state->priorterm = 1;
|
|
state->priortermval = (double)(0);
|
|
state->areatype = 0;
|
|
state->gridtype = 0;
|
|
state->smoothing = 0.0;
|
|
state->nlayers = 0;
|
|
state->solvertype = 1;
|
|
state->npoints = 0;
|
|
state->d = d;
|
|
state->sx = 1.0;
|
|
state->sy = 1.0;
|
|
state->lsqrcnt = 5;
|
|
|
|
/*
|
|
* Algorithm settings
|
|
*/
|
|
state->adddegreeoffreedom = ae_true;
|
|
state->maxcoresize = 16;
|
|
state->interfacesize = 5;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets constant prior term (model is a sum of bicubic spline
|
|
and global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
Constant prior term is determined by least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
V - value for user-defined prior
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetuserterm(spline2dbuilder* state,
|
|
double v,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(v, _state), "Spline2DBuilderSetUserTerm: infinite/NAN value passed", _state);
|
|
state->priorterm = 0;
|
|
state->priortermval = v;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets linear prior term (model is a sum of bicubic spline and
|
|
global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
Linear prior term is determined by least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetlinterm(spline2dbuilder* state, ae_state *_state)
|
|
{
|
|
|
|
|
|
state->priorterm = 1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets constant prior term (model is a sum of bicubic spline
|
|
and global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
Constant prior term is determined by least squares fitting.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetconstterm(spline2dbuilder* state, ae_state *_state)
|
|
{
|
|
|
|
|
|
state->priorterm = 2;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets zero prior term (model is a sum of bicubic spline and
|
|
global prior, which can be linear, constant, user-defined constant or
|
|
zero).
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline builder
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetzeroterm(spline2dbuilder* state, ae_state *_state)
|
|
{
|
|
|
|
|
|
state->priorterm = 3;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function adds dataset to the builder object.
|
|
|
|
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 - spline 2D builder object
|
|
XY - points, array[N,2+D]. One row corresponds to one point
|
|
in the dataset. First 2 elements are coordinates, next
|
|
D elements are function values. Array may be larger than
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetpoints(spline2dbuilder* state,
|
|
/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t ew;
|
|
|
|
|
|
ae_assert(n>0, "Spline2DBuilderSetPoints: N<0", _state);
|
|
ae_assert(xy->rows>=n, "Spline2DBuilderSetPoints: Rows(XY)<N", _state);
|
|
ae_assert(xy->cols>=2+state->d, "Spline2DBuilderSetPoints: Cols(XY)<NX+NY", _state);
|
|
ae_assert(apservisfinitematrix(xy, n, 2+state->d, _state), "Spline2DBuilderSetPoints: XY contains infinite or NaN values!", _state);
|
|
state->npoints = n;
|
|
ew = 2+state->d;
|
|
rvectorsetlengthatleast(&state->xy, n*ew, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=ew-1; j++)
|
|
{
|
|
state->xy.ptr.p_double[i*ew+j] = xy->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets area where 2D spline interpolant is built. "Auto" means
|
|
that area extent is determined automatically from dataset extent.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetareaauto(spline2dbuilder* state, ae_state *_state)
|
|
{
|
|
|
|
|
|
state->areatype = 0;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets area where 2D spline interpolant is built to
|
|
user-defined one: [XA,XB]*[YA,YB]
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
XA,XB - spatial extent in the first (X) dimension, XA<XB
|
|
YA,YB - spatial extent in the second (Y) dimension, YA<YB
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetarea(spline2dbuilder* state,
|
|
double xa,
|
|
double xb,
|
|
double ya,
|
|
double yb,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(xa, _state), "Spline2DBuilderSetArea: XA is not finite", _state);
|
|
ae_assert(ae_isfinite(xb, _state), "Spline2DBuilderSetArea: XB is not finite", _state);
|
|
ae_assert(ae_isfinite(ya, _state), "Spline2DBuilderSetArea: YA is not finite", _state);
|
|
ae_assert(ae_isfinite(yb, _state), "Spline2DBuilderSetArea: YB is not finite", _state);
|
|
ae_assert(ae_fp_less(xa,xb), "Spline2DBuilderSetArea: XA>=XB", _state);
|
|
ae_assert(ae_fp_less(ya,yb), "Spline2DBuilderSetArea: YA>=YB", _state);
|
|
state->areatype = 1;
|
|
state->xa = xa;
|
|
state->xb = xb;
|
|
state->ya = ya;
|
|
state->yb = yb;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets nodes count for 2D spline interpolant. Fitting is
|
|
performed on area defined with one of the "setarea" functions; this one
|
|
sets number of nodes placed upon the fitting area.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
KX - nodes count for the first (X) dimension; fitting interval
|
|
[XA,XB] is separated into KX-1 subintervals, with KX nodes
|
|
created at the boundaries.
|
|
KY - nodes count for the first (Y) dimension; fitting interval
|
|
[YA,YB] is separated into KY-1 subintervals, with KY nodes
|
|
created at the boundaries.
|
|
|
|
NOTE: at least 4 nodes is created in each dimension, so KX and KY are
|
|
silently increased if needed.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetgrid(spline2dbuilder* state,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(kx>0, "Spline2DBuilderSetGridSizePrecisely: KX<=0", _state);
|
|
ae_assert(ky>0, "Spline2DBuilderSetGridSizePrecisely: KY<=0", _state);
|
|
state->gridtype = 1;
|
|
state->kx = ae_maxint(kx, 4, _state);
|
|
state->ky = ae_maxint(ky, 4, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function allows you to choose least squares solver used to perform
|
|
fitting. This function sets solver algorithm to "FastDDM", which performs
|
|
fast parallel fitting by splitting problem into smaller chunks and merging
|
|
results together.
|
|
|
|
This solver is optimized for large-scale problems, starting from 256x256
|
|
grids, and up to 10000x10000 grids. Of course, it will work for smaller
|
|
grids too.
|
|
|
|
More detailed description of the algorithm is given below:
|
|
* algorithm generates hierarchy of nested grids, ranging from ~16x16
|
|
(topmost "layer" of the model) to ~KX*KY one (final layer). Upper layers
|
|
model global behavior of the function, lower layers are used to model
|
|
fine details. Moving from layer to layer doubles grid density.
|
|
* fitting is started from topmost layer, subsequent layers are fitted
|
|
using residuals from previous ones.
|
|
* user may choose to skip generation of upper layers and generate only a
|
|
few bottom ones, which will result in much better performance and
|
|
parallelization efficiency, at the cost of algorithm inability to "patch"
|
|
large holes in the dataset.
|
|
* every layer is regularized using progressively increasing regularization
|
|
coefficient; thus, increasing LambdaV penalizes fine details first,
|
|
leaving lower frequencies almost intact for a while.
|
|
* after fitting is done, all layers are merged together into one bicubic
|
|
spline
|
|
|
|
IMPORTANT: regularization coefficient used by this solver is different
|
|
from the one used by BlockLLS. Latter utilizes nonlinearity
|
|
penalty, which is global in nature (large regularization
|
|
results in global linear trend being extracted); this solver
|
|
uses another, localized form of penalty, which is suitable for
|
|
parallel processing.
|
|
|
|
Notes on memory and performance:
|
|
* memory requirements: most memory is consumed during modeling of the
|
|
higher layers; ~[512*NPoints] bytes is required for a model with full
|
|
hierarchy of grids being generated. However, if you skip a few topmost
|
|
layers, you will get nearly constant (wrt. points count and grid size)
|
|
memory consumption.
|
|
* serial running time: O(K*K)+O(NPoints) for a KxK grid
|
|
* parallelism potential: good. You may get nearly linear speed-up when
|
|
performing fitting with just a few layers. Adding more layers results in
|
|
model becoming more global, which somewhat reduces efficiency of the
|
|
parallel code.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
NLayers - number of layers in the model:
|
|
* NLayers>=1 means that up to chosen number of bottom
|
|
layers is fitted
|
|
* NLayers=0 means that maximum number of layers is chosen
|
|
(according to current grid size)
|
|
* NLayers<=-1 means that up to |NLayers| topmost layers is
|
|
skipped
|
|
Recommendations:
|
|
* good "default" value is 2 layers
|
|
* you may need more layers, if your dataset is very
|
|
irregular and you want to "patch" large holes. For a
|
|
grid step H (equal to AreaWidth/GridSize) you may expect
|
|
that last layer reproduces variations at distance H (and
|
|
can patch holes that wide); that higher layers operate
|
|
at distances 2*H, 4*H, 8*H and so on.
|
|
* good value for "bullletproof" mode is NLayers=0, which
|
|
results in complete hierarchy of layers being generated.
|
|
LambdaV - regularization coefficient, chosen in such a way that it
|
|
penalizes bottom layers (fine details) first.
|
|
LambdaV>=0, zero value means that no penalty is applied.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetalgofastddm(spline2dbuilder* state,
|
|
ae_int_t nlayers,
|
|
double lambdav,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(lambdav, _state), "Spline2DBuilderSetAlgoFastDDM: LambdaV is not finite value", _state);
|
|
ae_assert(ae_fp_greater_eq(lambdav,(double)(0)), "Spline2DBuilderSetAlgoFastDDM: LambdaV<0", _state);
|
|
state->solvertype = 3;
|
|
state->nlayers = nlayers;
|
|
state->smoothing = lambdav;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function allows you to choose least squares solver used to perform
|
|
fitting. This function sets solver algorithm to "BlockLLS", which performs
|
|
least squares fitting with fast sparse direct solver, with optional
|
|
nonsmoothness penalty being applied.
|
|
|
|
Nonlinearity penalty has the following form:
|
|
|
|
[ ]
|
|
P() ~ Lambda* integral[ (d2S/dx2)^2 + 2*(d2S/dxdy)^2 + (d2S/dy2)^2 ]dxdy
|
|
[ ]
|
|
|
|
here integral is calculated over entire grid, and "~" means "proportional"
|
|
because integral is normalized after calcilation. Extremely large values
|
|
of Lambda result in linear fit being performed.
|
|
|
|
NOTE: this algorithm is the most robust and controllable one, but it is
|
|
limited by 512x512 grids and (say) up to 1.000.000 points. However,
|
|
ALGLIB has one more spline solver: FastDDM algorithm, which is
|
|
intended for really large-scale problems (in 10M-100M range). FastDDM
|
|
algorithm also has better parallelism properties.
|
|
|
|
More information on BlockLLS solver:
|
|
* memory requirements: ~[32*K^3+256*NPoints] bytes for KxK grid with
|
|
NPoints-sized dataset
|
|
* serial running time: O(K^4+NPoints)
|
|
* parallelism potential: limited. You may get some sublinear gain when
|
|
working with large grids (K's in 256..512 range)
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
LambdaNS- non-negative value:
|
|
* positive value means that some smoothing is applied
|
|
* zero value means that no smoothing is applied, and
|
|
corresponding entries of design matrix are numerically
|
|
zero and dropped from consideration.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetalgoblocklls(spline2dbuilder* state,
|
|
double lambdans,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(lambdans, _state), "Spline2DBuilderSetAlgoBlockLLS: LambdaNS is not finite value", _state);
|
|
ae_assert(ae_fp_greater_eq(lambdans,(double)(0)), "Spline2DBuilderSetAlgoBlockLLS: LambdaNS<0", _state);
|
|
state->solvertype = 1;
|
|
state->smoothing = lambdans;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function allows you to choose least squares solver used to perform
|
|
fitting. This function sets solver algorithm to "NaiveLLS".
|
|
|
|
IMPORTANT: NaiveLLS is NOT intended to be used in real life code! This
|
|
algorithm solves problem by generated dense (K^2)x(K^2+NPoints)
|
|
matrix and solves linear least squares problem with dense
|
|
solver.
|
|
|
|
It is here just to test BlockLLS against reference solver
|
|
(and maybe for someone trying to compare well optimized solver
|
|
against straightforward approach to the LLS problem).
|
|
|
|
More information on naive LLS solver:
|
|
* memory requirements: ~[8*K^4+256*NPoints] bytes for KxK grid.
|
|
* serial running time: O(K^6+NPoints) for KxK grid
|
|
* when compared with BlockLLS, NaiveLLS has ~K larger memory demand and
|
|
~K^2 larger running time.
|
|
|
|
INPUT PARAMETERS:
|
|
S - spline 2D builder object
|
|
LambdaNS- nonsmoothness penalty
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dbuildersetalgonaivells(spline2dbuilder* state,
|
|
double lambdans,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(lambdans, _state), "Spline2DBuilderSetAlgoBlockLLS: LambdaNS is not finite value", _state);
|
|
ae_assert(ae_fp_greater_eq(lambdans,(double)(0)), "Spline2DBuilderSetAlgoBlockLLS: LambdaNS<0", _state);
|
|
state->solvertype = 2;
|
|
state->smoothing = lambdans;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function fits bicubic spline to current dataset, using current area/
|
|
grid and current LLS solver.
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
! * hardware vendor (Intel) implementations of linear algebra primitives
|
|
! (C++ and C# versions, x86/x64 platform)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
INPUT PARAMETERS:
|
|
State - spline 2D builder object
|
|
|
|
OUTPUT PARAMETERS:
|
|
S - 2D spline, fit result
|
|
Rep - fitting report, which provides some additional info about
|
|
errors, R2 coefficient and so on.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dfit(spline2dbuilder* state,
|
|
spline2dinterpolant* s,
|
|
spline2dfitreport* rep,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
double xa;
|
|
double xb;
|
|
double ya;
|
|
double yb;
|
|
double xaraw;
|
|
double xbraw;
|
|
double yaraw;
|
|
double ybraw;
|
|
ae_int_t kx;
|
|
ae_int_t ky;
|
|
double hx;
|
|
double hy;
|
|
double invhx;
|
|
double invhy;
|
|
ae_int_t gridexpansion;
|
|
ae_int_t nzwidth;
|
|
ae_int_t bfrad;
|
|
ae_int_t npoints;
|
|
ae_int_t d;
|
|
ae_int_t ew;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
double v;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
double vx;
|
|
double vy;
|
|
ae_int_t arows;
|
|
ae_int_t acopied;
|
|
ae_int_t basecasex;
|
|
ae_int_t basecasey;
|
|
double eps;
|
|
ae_vector xywork;
|
|
ae_matrix vterm;
|
|
ae_vector tmpx;
|
|
ae_vector tmpy;
|
|
ae_vector tmp0;
|
|
ae_vector tmp1;
|
|
ae_vector meany;
|
|
ae_vector xyindex;
|
|
ae_vector tmpi;
|
|
spline1dinterpolant basis1;
|
|
sparsematrix av;
|
|
sparsematrix ah;
|
|
spline2dxdesignmatrix xdesignmatrix;
|
|
ae_vector z;
|
|
spline2dblockllsbuf blockllsbuf;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
double tss;
|
|
ae_int_t dstidx;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&xywork, 0, sizeof(xywork));
|
|
memset(&vterm, 0, sizeof(vterm));
|
|
memset(&tmpx, 0, sizeof(tmpx));
|
|
memset(&tmpy, 0, sizeof(tmpy));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
memset(&tmp1, 0, sizeof(tmp1));
|
|
memset(&meany, 0, sizeof(meany));
|
|
memset(&xyindex, 0, sizeof(xyindex));
|
|
memset(&tmpi, 0, sizeof(tmpi));
|
|
memset(&basis1, 0, sizeof(basis1));
|
|
memset(&av, 0, sizeof(av));
|
|
memset(&ah, 0, sizeof(ah));
|
|
memset(&xdesignmatrix, 0, sizeof(xdesignmatrix));
|
|
memset(&z, 0, sizeof(z));
|
|
memset(&blockllsbuf, 0, sizeof(blockllsbuf));
|
|
_spline2dinterpolant_clear(s);
|
|
_spline2dfitreport_clear(rep);
|
|
ae_vector_init(&xywork, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&vterm, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpx, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&tmpy, 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(&meany, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xyindex, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&tmpi, 0, DT_INT, _state, ae_true);
|
|
_spline1dinterpolant_init(&basis1, _state, ae_true);
|
|
_sparsematrix_init(&av, _state, ae_true);
|
|
_sparsematrix_init(&ah, _state, ae_true);
|
|
_spline2dxdesignmatrix_init(&xdesignmatrix, _state, ae_true);
|
|
ae_vector_init(&z, 0, DT_REAL, _state, ae_true);
|
|
_spline2dblockllsbuf_init(&blockllsbuf, _state, ae_true);
|
|
|
|
nzwidth = 4;
|
|
bfrad = 2;
|
|
npoints = state->npoints;
|
|
d = state->d;
|
|
ew = 2+d;
|
|
|
|
/*
|
|
* Integrity checks
|
|
*/
|
|
ae_assert(ae_fp_eq(state->sx,(double)(1)), "Spline2DFit: integrity error", _state);
|
|
ae_assert(ae_fp_eq(state->sy,(double)(1)), "Spline2DFit: integrity error", _state);
|
|
|
|
/*
|
|
* Determine actual area size and grid step
|
|
*
|
|
* NOTE: initialize vars by zeros in order to avoid spurious
|
|
* compiler warnings.
|
|
*/
|
|
xa = (double)(0);
|
|
xb = (double)(0);
|
|
ya = (double)(0);
|
|
yb = (double)(0);
|
|
if( state->areatype==0 )
|
|
{
|
|
if( npoints>0 )
|
|
{
|
|
xa = state->xy.ptr.p_double[0];
|
|
xb = state->xy.ptr.p_double[0];
|
|
ya = state->xy.ptr.p_double[1];
|
|
yb = state->xy.ptr.p_double[1];
|
|
for(i=1; i<=npoints-1; i++)
|
|
{
|
|
xa = ae_minreal(xa, state->xy.ptr.p_double[i*ew+0], _state);
|
|
xb = ae_maxreal(xb, state->xy.ptr.p_double[i*ew+0], _state);
|
|
ya = ae_minreal(ya, state->xy.ptr.p_double[i*ew+1], _state);
|
|
yb = ae_maxreal(yb, state->xy.ptr.p_double[i*ew+1], _state);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
xa = (double)(-1);
|
|
xb = (double)(1);
|
|
ya = (double)(-1);
|
|
yb = (double)(1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( state->areatype==1 )
|
|
{
|
|
xa = state->xa;
|
|
xb = state->xb;
|
|
ya = state->ya;
|
|
yb = state->yb;
|
|
}
|
|
else
|
|
{
|
|
ae_assert(ae_false, "Assertion failed", _state);
|
|
}
|
|
}
|
|
if( ae_fp_eq(xa,xb) )
|
|
{
|
|
v = xa;
|
|
if( ae_fp_greater_eq(v,(double)(0)) )
|
|
{
|
|
xa = v/2-1;
|
|
xb = v*2+1;
|
|
}
|
|
else
|
|
{
|
|
xa = v*2-1;
|
|
xb = v/2+1;
|
|
}
|
|
}
|
|
if( ae_fp_eq(ya,yb) )
|
|
{
|
|
v = ya;
|
|
if( ae_fp_greater_eq(v,(double)(0)) )
|
|
{
|
|
ya = v/2-1;
|
|
yb = v*2+1;
|
|
}
|
|
else
|
|
{
|
|
ya = v*2-1;
|
|
yb = v/2+1;
|
|
}
|
|
}
|
|
ae_assert(ae_fp_less(xa,xb), "Spline2DFit: integrity error", _state);
|
|
ae_assert(ae_fp_less(ya,yb), "Spline2DFit: integrity error", _state);
|
|
kx = 0;
|
|
ky = 0;
|
|
if( state->gridtype==0 )
|
|
{
|
|
kx = 4;
|
|
ky = 4;
|
|
}
|
|
else
|
|
{
|
|
if( state->gridtype==1 )
|
|
{
|
|
kx = state->kx;
|
|
ky = state->ky;
|
|
}
|
|
else
|
|
{
|
|
ae_assert(ae_false, "Assertion failed", _state);
|
|
}
|
|
}
|
|
ae_assert(kx>0, "Spline2DFit: integrity error", _state);
|
|
ae_assert(ky>0, "Spline2DFit: integrity error", _state);
|
|
basecasex = -1;
|
|
basecasey = -1;
|
|
if( state->solvertype==3 )
|
|
{
|
|
|
|
/*
|
|
* Large-scale solver with special requirements to grid size.
|
|
*/
|
|
kx = ae_maxint(kx, nzwidth, _state);
|
|
ky = ae_maxint(ky, nzwidth, _state);
|
|
k = 1;
|
|
while(imin2(kx, ky, _state)>state->maxcoresize+1)
|
|
{
|
|
kx = idivup(kx-1, 2, _state)+1;
|
|
ky = idivup(ky-1, 2, _state)+1;
|
|
k = k+1;
|
|
}
|
|
basecasex = kx-1;
|
|
k0 = 1;
|
|
while(kx>state->maxcoresize+1)
|
|
{
|
|
basecasex = idivup(kx-1, 2, _state);
|
|
kx = basecasex+1;
|
|
k0 = k0+1;
|
|
}
|
|
while(k0>1)
|
|
{
|
|
kx = (kx-1)*2+1;
|
|
k0 = k0-1;
|
|
}
|
|
basecasey = ky-1;
|
|
k1 = 1;
|
|
while(ky>state->maxcoresize+1)
|
|
{
|
|
basecasey = idivup(ky-1, 2, _state);
|
|
ky = basecasey+1;
|
|
k1 = k1+1;
|
|
}
|
|
while(k1>1)
|
|
{
|
|
ky = (ky-1)*2+1;
|
|
k1 = k1-1;
|
|
}
|
|
while(k>1)
|
|
{
|
|
kx = (kx-1)*2+1;
|
|
ky = (ky-1)*2+1;
|
|
k = k-1;
|
|
}
|
|
|
|
/*
|
|
* Grid is NOT expanded. We have very strict requirements on
|
|
* grid size, and we do not want to overcomplicate it by
|
|
* playing with grid size in order to add one more degree of
|
|
* freedom. It is not relevant for such large tasks.
|
|
*/
|
|
gridexpansion = 0;
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Medium-scale solvers which are tolerant to grid size.
|
|
*/
|
|
kx = ae_maxint(kx, nzwidth, _state);
|
|
ky = ae_maxint(ky, nzwidth, _state);
|
|
|
|
/*
|
|
* Grid is expanded by 1 in order to add one more effective degree
|
|
* of freedom to the spline. Having additional nodes outside of the
|
|
* area allows us to emulate changes in the derivative at the bound
|
|
* without having specialized "boundary" version of the basis function.
|
|
*/
|
|
if( state->adddegreeoffreedom )
|
|
{
|
|
gridexpansion = 1;
|
|
}
|
|
else
|
|
{
|
|
gridexpansion = 0;
|
|
}
|
|
}
|
|
hx = coalesce(xb-xa, 1.0, _state)/(kx-1);
|
|
hy = coalesce(yb-ya, 1.0, _state)/(ky-1);
|
|
invhx = 1/hx;
|
|
invhy = 1/hy;
|
|
|
|
/*
|
|
* We determined "raw" grid size. Now perform a grid correction according
|
|
* to current grid expansion size.
|
|
*/
|
|
xaraw = xa;
|
|
yaraw = ya;
|
|
xbraw = xb;
|
|
ybraw = yb;
|
|
xa = xa-hx*gridexpansion;
|
|
ya = ya-hy*gridexpansion;
|
|
xb = xb+hx*gridexpansion;
|
|
yb = yb+hy*gridexpansion;
|
|
kx = kx+2*gridexpansion;
|
|
ky = ky+2*gridexpansion;
|
|
|
|
/*
|
|
* Create output spline using transformed (unit-scale)
|
|
* coordinates, fill by zero values
|
|
*/
|
|
s->d = d;
|
|
s->n = kx;
|
|
s->m = ky;
|
|
s->stype = -3;
|
|
sfx = s->n*s->m*d;
|
|
sfy = 2*s->n*s->m*d;
|
|
sfxy = 3*s->n*s->m*d;
|
|
ae_vector_set_length(&s->x, s->n, _state);
|
|
ae_vector_set_length(&s->y, s->m, _state);
|
|
ae_vector_set_length(&s->f, 4*s->n*s->m*d, _state);
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
s->x.ptr.p_double[i] = (double)(i);
|
|
}
|
|
for(i=0; i<=s->m-1; i++)
|
|
{
|
|
s->y.ptr.p_double[i] = (double)(i);
|
|
}
|
|
for(i=0; i<=4*s->n*s->m*d-1; i++)
|
|
{
|
|
s->f.ptr.p_double[i] = 0.0;
|
|
}
|
|
|
|
/*
|
|
* Create local copy of dataset (only points in the grid are copied;
|
|
* we allow small step out of the grid, by Eps*H, in order to deal
|
|
* with numerical rounding errors).
|
|
*
|
|
* An additional copy of Y-values is created at columns beyond 2+J;
|
|
* it is preserved during all transformations. This copy is used
|
|
* to calculate error-related metrics.
|
|
*
|
|
* Calculate mean(Y), TSS
|
|
*/
|
|
ae_vector_set_length(&meany, d, _state);
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
meany.ptr.p_double[j] = (double)(0);
|
|
}
|
|
rvectorsetlengthatleast(&xywork, npoints*ew, _state);
|
|
acopied = 0;
|
|
eps = 1.0E-6;
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
vx = state->xy.ptr.p_double[i*ew+0];
|
|
vy = state->xy.ptr.p_double[i*ew+1];
|
|
if( ((ae_fp_less_eq(xaraw-eps*hx,vx)&&ae_fp_less_eq(vx,xbraw+eps*hx))&&ae_fp_less_eq(yaraw-eps*hy,vy))&&ae_fp_less_eq(vy,ybraw+eps*hy) )
|
|
{
|
|
xywork.ptr.p_double[acopied*ew+0] = (vx-xa)*invhx;
|
|
xywork.ptr.p_double[acopied*ew+1] = (vy-ya)*invhy;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
v = state->xy.ptr.p_double[i*ew+2+j];
|
|
xywork.ptr.p_double[acopied*ew+2+j] = v;
|
|
meany.ptr.p_double[j] = meany.ptr.p_double[j]+v;
|
|
}
|
|
acopied = acopied+1;
|
|
}
|
|
}
|
|
npoints = acopied;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
meany.ptr.p_double[j] = meany.ptr.p_double[j]/coalesce((double)(npoints), (double)(1), _state);
|
|
}
|
|
tss = 0.0;
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
tss = tss+ae_sqr(xywork.ptr.p_double[i*ew+2+j]-meany.ptr.p_double[j], _state);
|
|
}
|
|
}
|
|
tss = coalesce(tss, 1.0, _state);
|
|
|
|
/*
|
|
* Handle prior term.
|
|
* Modify output spline.
|
|
* Quick exit if dataset is empty.
|
|
*/
|
|
buildpriorterm1(&xywork, npoints, 2, d, state->priorterm, state->priortermval, &vterm, _state);
|
|
if( npoints==0 )
|
|
{
|
|
|
|
/*
|
|
* Quick exit
|
|
*/
|
|
for(k=0; k<=s->n*s->m-1; k++)
|
|
{
|
|
k0 = k%s->n;
|
|
k1 = k/s->n;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
dstidx = d*(k1*s->n+k0)+j;
|
|
s->f.ptr.p_double[dstidx] = s->f.ptr.p_double[dstidx]+vterm.ptr.pp_double[j][0]*s->x.ptr.p_double[k0]+vterm.ptr.pp_double[j][1]*s->y.ptr.p_double[k1]+vterm.ptr.pp_double[j][2];
|
|
s->f.ptr.p_double[sfx+dstidx] = s->f.ptr.p_double[sfx+dstidx]+vterm.ptr.pp_double[j][0];
|
|
s->f.ptr.p_double[sfy+dstidx] = s->f.ptr.p_double[sfy+dstidx]+vterm.ptr.pp_double[j][1];
|
|
}
|
|
}
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
s->x.ptr.p_double[i] = s->x.ptr.p_double[i]*hx+xa;
|
|
}
|
|
for(i=0; i<=s->m-1; i++)
|
|
{
|
|
s->y.ptr.p_double[i] = s->y.ptr.p_double[i]*hy+ya;
|
|
}
|
|
for(i=0; i<=s->n*s->m*d-1; i++)
|
|
{
|
|
s->f.ptr.p_double[sfx+i] = s->f.ptr.p_double[sfx+i]*invhx;
|
|
s->f.ptr.p_double[sfy+i] = s->f.ptr.p_double[sfy+i]*invhy;
|
|
s->f.ptr.p_double[sfxy+i] = s->f.ptr.p_double[sfxy+i]*invhx*invhy;
|
|
}
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rep->r2 = 1.0;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Build 1D compact basis function
|
|
* Generate design matrix
|
|
*/
|
|
ae_vector_set_length(&tmpx, 7, _state);
|
|
ae_vector_set_length(&tmpy, 7, _state);
|
|
tmpx.ptr.p_double[0] = (double)(-3);
|
|
tmpx.ptr.p_double[1] = (double)(-2);
|
|
tmpx.ptr.p_double[2] = (double)(-1);
|
|
tmpx.ptr.p_double[3] = (double)(0);
|
|
tmpx.ptr.p_double[4] = (double)(1);
|
|
tmpx.ptr.p_double[5] = (double)(2);
|
|
tmpx.ptr.p_double[6] = (double)(3);
|
|
tmpy.ptr.p_double[0] = (double)(0);
|
|
tmpy.ptr.p_double[1] = (double)(0);
|
|
tmpy.ptr.p_double[2] = (double)1/(double)12;
|
|
tmpy.ptr.p_double[3] = (double)2/(double)6;
|
|
tmpy.ptr.p_double[4] = (double)1/(double)12;
|
|
tmpy.ptr.p_double[5] = (double)(0);
|
|
tmpy.ptr.p_double[6] = (double)(0);
|
|
spline1dbuildcubic(&tmpx, &tmpy, tmpx.cnt, 2, 0.0, 2, 0.0, &basis1, _state);
|
|
|
|
/*
|
|
* Solve.
|
|
* Update spline.
|
|
*/
|
|
if( state->solvertype==1 )
|
|
{
|
|
|
|
/*
|
|
* BlockLLS
|
|
*/
|
|
spline2d_reorderdatasetandbuildindex(&xywork, npoints, d, &tmp0, 0, kx, ky, &xyindex, &tmpi, _state);
|
|
spline2d_xdesigngenerate(&xywork, &xyindex, 0, kx, kx, 0, ky, ky, d, spline2d_lambdaregblocklls, state->smoothing, &basis1, &xdesignmatrix, _state);
|
|
spline2d_blockllsfit(&xdesignmatrix, state->lsqrcnt, &z, rep, tss, &blockllsbuf, _state);
|
|
spline2d_updatesplinetable(&z, kx, ky, d, &basis1, bfrad, &s->f, s->m, s->n, 1, _state);
|
|
}
|
|
else
|
|
{
|
|
if( state->solvertype==2 )
|
|
{
|
|
|
|
/*
|
|
* NaiveLLS, reference implementation
|
|
*/
|
|
spline2d_generatedesignmatrix(&xywork, npoints, d, kx, ky, state->smoothing, spline2d_lambdaregblocklls, &basis1, &av, &ah, &arows, _state);
|
|
spline2d_naivellsfit(&av, &ah, arows, &xywork, kx, ky, npoints, d, state->lsqrcnt, &z, rep, tss, _state);
|
|
spline2d_updatesplinetable(&z, kx, ky, d, &basis1, bfrad, &s->f, s->m, s->n, 1, _state);
|
|
}
|
|
else
|
|
{
|
|
if( state->solvertype==3 )
|
|
{
|
|
|
|
/*
|
|
* FastDDM method
|
|
*/
|
|
ae_assert(basecasex>0, "Spline2DFit: integrity error", _state);
|
|
ae_assert(basecasey>0, "Spline2DFit: integrity error", _state);
|
|
spline2d_fastddmfit(&xywork, npoints, d, kx, ky, basecasex, basecasey, state->maxcoresize, state->interfacesize, state->nlayers, state->smoothing, state->lsqrcnt, &basis1, s, rep, tss, _state);
|
|
}
|
|
else
|
|
{
|
|
ae_assert(ae_false, "Spline2DFit: integrity error", _state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Append prior term.
|
|
* Transform spline to original coordinates
|
|
*/
|
|
for(k=0; k<=s->n*s->m-1; k++)
|
|
{
|
|
k0 = k%s->n;
|
|
k1 = k/s->n;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
dstidx = d*(k1*s->n+k0)+j;
|
|
s->f.ptr.p_double[dstidx] = s->f.ptr.p_double[dstidx]+vterm.ptr.pp_double[j][0]*s->x.ptr.p_double[k0]+vterm.ptr.pp_double[j][1]*s->y.ptr.p_double[k1]+vterm.ptr.pp_double[j][2];
|
|
s->f.ptr.p_double[sfx+dstidx] = s->f.ptr.p_double[sfx+dstidx]+vterm.ptr.pp_double[j][0];
|
|
s->f.ptr.p_double[sfy+dstidx] = s->f.ptr.p_double[sfy+dstidx]+vterm.ptr.pp_double[j][1];
|
|
}
|
|
}
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
s->x.ptr.p_double[i] = s->x.ptr.p_double[i]*hx+xa;
|
|
}
|
|
for(i=0; i<=s->m-1; i++)
|
|
{
|
|
s->y.ptr.p_double[i] = s->y.ptr.p_double[i]*hy+ya;
|
|
}
|
|
for(i=0; i<=s->n*s->m*d-1; i++)
|
|
{
|
|
s->f.ptr.p_double[sfx+i] = s->f.ptr.p_double[sfx+i]*invhx;
|
|
s->f.ptr.p_double[sfy+i] = s->f.ptr.p_double[sfy+i]*invhy;
|
|
s->f.ptr.p_double[sfxy+i] = s->f.ptr.p_double[sfxy+i]*invhx*invhy;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: allocation
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dalloc(ae_serializer* s,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
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);
|
|
allocrealarray(s, &spline->x, -1, _state);
|
|
allocrealarray(s, &spline->y, -1, _state);
|
|
allocrealarray(s, &spline->f, -1, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: serialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dserialize(ae_serializer* s,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_serialize_int(s, getspline2dserializationcode(_state), _state);
|
|
|
|
/*
|
|
* Data
|
|
*/
|
|
ae_serializer_serialize_int(s, spline->stype, _state);
|
|
ae_serializer_serialize_int(s, spline->n, _state);
|
|
ae_serializer_serialize_int(s, spline->m, _state);
|
|
ae_serializer_serialize_int(s, spline->d, _state);
|
|
serializerealarray(s, &spline->x, -1, _state);
|
|
serializerealarray(s, &spline->y, -1, _state);
|
|
serializerealarray(s, &spline->f, -1, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: unserialization
|
|
|
|
-- ALGLIB --
|
|
Copyright 28.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void spline2dunserialize(ae_serializer* s,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t scode;
|
|
|
|
_spline2dinterpolant_clear(spline);
|
|
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_unserialize_int(s, &scode, _state);
|
|
ae_assert(scode==getspline2dserializationcode(_state), "Spline2DUnserialize: stream header corrupted", _state);
|
|
|
|
/*
|
|
* Data
|
|
*/
|
|
ae_serializer_unserialize_int(s, &spline->stype, _state);
|
|
ae_serializer_unserialize_int(s, &spline->n, _state);
|
|
ae_serializer_unserialize_int(s, &spline->m, _state);
|
|
ae_serializer_unserialize_int(s, &spline->d, _state);
|
|
unserializerealarray(s, &spline->x, _state);
|
|
unserializerealarray(s, &spline->y, _state);
|
|
unserializerealarray(s, &spline->f, _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);
|
|
memset(&xt, 0, sizeof(xt));
|
|
memset(&ft, 0, sizeof(ft));
|
|
memset(&c, 0, sizeof(c));
|
|
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);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function generates design matrix for the problem (in fact, two design
|
|
matrices are generated: "vertical" one and transposed (horizontal) one.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints*(2+D)]; dataset after scaling in such
|
|
way that grid step is equal to 1.0 in both dimensions.
|
|
NPoints - dataset size, NPoints>=1
|
|
KX, KY - grid size, KX,KY>=4
|
|
Smoothing - nonlinearity penalty coefficient, >=0
|
|
LambdaReg - regularization coefficient, >=0
|
|
Basis1 - basis spline, expected to be non-zero only at [-2,+2]
|
|
AV, AH - possibly preallocated buffers
|
|
|
|
OUTPUT PARAMETERS:
|
|
AV - sparse matrix[ARows,KX*KY]; design matrix
|
|
AH - transpose of AV
|
|
ARows - number of rows in design matrix
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_generatedesignmatrix(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
double smoothing,
|
|
double lambdareg,
|
|
spline1dinterpolant* basis1,
|
|
sparsematrix* av,
|
|
sparsematrix* ah,
|
|
ae_int_t* arows,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t nzwidth;
|
|
ae_int_t nzshift;
|
|
ae_int_t ew;
|
|
ae_int_t i;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
ae_int_t dstidx;
|
|
double v;
|
|
double v0;
|
|
double v1;
|
|
double v2;
|
|
double w0;
|
|
double w1;
|
|
double w2;
|
|
ae_vector crx;
|
|
ae_vector cry;
|
|
ae_vector nrs;
|
|
ae_matrix d2x;
|
|
ae_matrix d2y;
|
|
ae_matrix dxy;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&crx, 0, sizeof(crx));
|
|
memset(&cry, 0, sizeof(cry));
|
|
memset(&nrs, 0, sizeof(nrs));
|
|
memset(&d2x, 0, sizeof(d2x));
|
|
memset(&d2y, 0, sizeof(d2y));
|
|
memset(&dxy, 0, sizeof(dxy));
|
|
*arows = 0;
|
|
ae_vector_init(&crx, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&cry, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&nrs, 0, DT_INT, _state, ae_true);
|
|
ae_matrix_init(&d2x, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&d2y, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&dxy, 0, 0, DT_REAL, _state, ae_true);
|
|
|
|
nzwidth = 4;
|
|
nzshift = 1;
|
|
ae_assert(npoints>0, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(kx>=nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(ky>=nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
ew = 2+d;
|
|
|
|
/*
|
|
* Determine canonical rectangle for every point. Every point of the dataset is
|
|
* influenced by at most NZWidth*NZWidth basis functions, which form NZWidth*NZWidth
|
|
* canonical rectangle.
|
|
*
|
|
* Thus, we have (KX-NZWidth+1)*(KY-NZWidth+1) overlapping canonical rectangles.
|
|
* Assigning every point to its rectangle simplifies creation of sparse basis
|
|
* matrix at the next steps.
|
|
*/
|
|
ae_vector_set_length(&crx, npoints, _state);
|
|
ae_vector_set_length(&cry, npoints, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
crx.ptr.p_int[i] = iboundval(ae_ifloor(xy->ptr.p_double[i*ew+0], _state)-nzshift, 0, kx-nzwidth, _state);
|
|
cry.ptr.p_int[i] = iboundval(ae_ifloor(xy->ptr.p_double[i*ew+1], _state)-nzshift, 0, ky-nzwidth, _state);
|
|
}
|
|
|
|
/*
|
|
* Create vertical and horizontal design matrices
|
|
*/
|
|
*arows = npoints+kx*ky;
|
|
if( ae_fp_neq(smoothing,0.0) )
|
|
{
|
|
ae_assert(ae_fp_greater(smoothing,0.0), "Spline2DFit: integrity check failed", _state);
|
|
*arows = *arows+3*(kx-2)*(ky-2);
|
|
}
|
|
ae_vector_set_length(&nrs, *arows, _state);
|
|
dstidx = 0;
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
nrs.ptr.p_int[dstidx+i] = nzwidth*nzwidth;
|
|
}
|
|
dstidx = dstidx+npoints;
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
nrs.ptr.p_int[dstidx+i] = 1;
|
|
}
|
|
dstidx = dstidx+kx*ky;
|
|
if( ae_fp_neq(smoothing,0.0) )
|
|
{
|
|
for(i=0; i<=3*(kx-2)*(ky-2)-1; i++)
|
|
{
|
|
nrs.ptr.p_int[dstidx+i] = 3*3;
|
|
}
|
|
dstidx = dstidx+3*(kx-2)*(ky-2);
|
|
}
|
|
ae_assert(dstidx==(*arows), "Spline2DFit: integrity check failed", _state);
|
|
sparsecreatecrs(*arows, kx*ky, &nrs, av, _state);
|
|
dstidx = 0;
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j1=0; j1<=nzwidth-1; j1++)
|
|
{
|
|
for(j0=0; j0<=nzwidth-1; j0++)
|
|
{
|
|
v0 = spline1dcalc(basis1, xy->ptr.p_double[i*ew+0]-(crx.ptr.p_int[i]+j0), _state);
|
|
v1 = spline1dcalc(basis1, xy->ptr.p_double[i*ew+1]-(cry.ptr.p_int[i]+j1), _state);
|
|
sparseset(av, dstidx+i, (cry.ptr.p_int[i]+j1)*kx+(crx.ptr.p_int[i]+j0), v0*v1, _state);
|
|
}
|
|
}
|
|
}
|
|
dstidx = dstidx+npoints;
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
sparseset(av, dstidx+i, i, lambdareg, _state);
|
|
}
|
|
dstidx = dstidx+kx*ky;
|
|
if( ae_fp_neq(smoothing,0.0) )
|
|
{
|
|
|
|
/*
|
|
* Smoothing is applied. Because all grid nodes are same,
|
|
* we apply same smoothing kernel, which is calculated only
|
|
* once at the beginning of design matrix generation.
|
|
*/
|
|
ae_matrix_set_length(&d2x, 3, 3, _state);
|
|
ae_matrix_set_length(&d2y, 3, 3, _state);
|
|
ae_matrix_set_length(&dxy, 3, 3, _state);
|
|
for(j1=0; j1<=2; j1++)
|
|
{
|
|
for(j0=0; j0<=2; j0++)
|
|
{
|
|
d2x.ptr.pp_double[j0][j1] = 0.0;
|
|
d2y.ptr.pp_double[j0][j1] = 0.0;
|
|
dxy.ptr.pp_double[j0][j1] = 0.0;
|
|
}
|
|
}
|
|
for(k1=0; k1<=2; k1++)
|
|
{
|
|
for(k0=0; k0<=2; k0++)
|
|
{
|
|
spline1ddiff(basis1, (double)(-(k0-1)), &v0, &v1, &v2, _state);
|
|
spline1ddiff(basis1, (double)(-(k1-1)), &w0, &w1, &w2, _state);
|
|
d2x.ptr.pp_double[k0][k1] = d2x.ptr.pp_double[k0][k1]+v2*w0;
|
|
d2y.ptr.pp_double[k0][k1] = d2y.ptr.pp_double[k0][k1]+w2*v0;
|
|
dxy.ptr.pp_double[k0][k1] = dxy.ptr.pp_double[k0][k1]+v1*w1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now, kernel is ready - apply it to all inner nodes of the grid.
|
|
*/
|
|
for(j1=1; j1<=ky-2; j1++)
|
|
{
|
|
for(j0=1; j0<=kx-2; j0++)
|
|
{
|
|
|
|
/*
|
|
* d2F/dx2 term
|
|
*/
|
|
v = smoothing;
|
|
for(k1=-1; k1<=1; k1++)
|
|
{
|
|
for(k0=-1; k0<=1; k0++)
|
|
{
|
|
sparseset(av, dstidx, (j1+k1)*kx+(j0+k0), v*d2x.ptr.pp_double[1+k0][1+k1], _state);
|
|
}
|
|
}
|
|
dstidx = dstidx+1;
|
|
|
|
/*
|
|
* d2F/dy2 term
|
|
*/
|
|
v = smoothing;
|
|
for(k1=-1; k1<=1; k1++)
|
|
{
|
|
for(k0=-1; k0<=1; k0++)
|
|
{
|
|
sparseset(av, dstidx, (j1+k1)*kx+(j0+k0), v*d2y.ptr.pp_double[1+k0][1+k1], _state);
|
|
}
|
|
}
|
|
dstidx = dstidx+1;
|
|
|
|
/*
|
|
* 2*d2F/dxdy term
|
|
*/
|
|
v = ae_sqrt((double)(2), _state)*smoothing;
|
|
for(k1=-1; k1<=1; k1++)
|
|
{
|
|
for(k0=-1; k0<=1; k0++)
|
|
{
|
|
sparseset(av, dstidx, (j1+k1)*kx+(j0+k0), v*dxy.ptr.pp_double[1+k0][1+k1], _state);
|
|
}
|
|
}
|
|
dstidx = dstidx+1;
|
|
}
|
|
}
|
|
}
|
|
ae_assert(dstidx==(*arows), "Spline2DFit: integrity check failed", _state);
|
|
sparsecopy(av, ah, _state);
|
|
sparsetransposecrs(ah, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function updates table of spline values/derivatives using coefficients
|
|
for a layer of basis functions.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_updatesplinetable(/* Real */ ae_vector* z,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t d,
|
|
spline1dinterpolant* basis1,
|
|
ae_int_t bfrad,
|
|
/* Real */ ae_vector* ftbl,
|
|
ae_int_t m,
|
|
ae_int_t n,
|
|
ae_int_t scalexy,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t k;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
ae_int_t j;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t j0a;
|
|
ae_int_t j0b;
|
|
ae_int_t j1a;
|
|
ae_int_t j1b;
|
|
double v;
|
|
double v0;
|
|
double v1;
|
|
double v01;
|
|
double v11;
|
|
double rdummy;
|
|
ae_int_t dstidx;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
double invscalexy;
|
|
|
|
|
|
ae_assert(n==(kx-1)*scalexy+1, "Spline2DFit.UpdateSplineTable: integrity check failed", _state);
|
|
ae_assert(m==(ky-1)*scalexy+1, "Spline2DFit.UpdateSplineTable: integrity check failed", _state);
|
|
invscalexy = (double)1/(double)scalexy;
|
|
sfx = n*m*d;
|
|
sfy = 2*n*m*d;
|
|
sfxy = 3*n*m*d;
|
|
for(k=0; k<=kx*ky-1; k++)
|
|
{
|
|
k0 = k%kx;
|
|
k1 = k/kx;
|
|
j0a = iboundval(k0*scalexy-(bfrad*scalexy-1), 0, n-1, _state);
|
|
j0b = iboundval(k0*scalexy+(bfrad*scalexy-1), 0, n-1, _state);
|
|
j1a = iboundval(k1*scalexy-(bfrad*scalexy-1), 0, m-1, _state);
|
|
j1b = iboundval(k1*scalexy+(bfrad*scalexy-1), 0, m-1, _state);
|
|
for(j1=j1a; j1<=j1b; j1++)
|
|
{
|
|
spline1ddiff(basis1, (j1-k1*scalexy)*invscalexy, &v1, &v11, &rdummy, _state);
|
|
v11 = v11*invscalexy;
|
|
for(j0=j0a; j0<=j0b; j0++)
|
|
{
|
|
spline1ddiff(basis1, (j0-k0*scalexy)*invscalexy, &v0, &v01, &rdummy, _state);
|
|
v01 = v01*invscalexy;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
dstidx = d*(j1*n+j0)+j;
|
|
v = z->ptr.p_double[j*kx*ky+k];
|
|
ftbl->ptr.p_double[dstidx] = ftbl->ptr.p_double[dstidx]+v0*v1*v;
|
|
ftbl->ptr.p_double[sfx+dstidx] = ftbl->ptr.p_double[sfx+dstidx]+v01*v1*v;
|
|
ftbl->ptr.p_double[sfy+dstidx] = ftbl->ptr.p_double[sfy+dstidx]+v0*v11*v;
|
|
ftbl->ptr.p_double[sfxy+dstidx] = ftbl->ptr.p_double[sfxy+dstidx]+v01*v11*v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs fitting with FastDDM solver.
|
|
Internal function, never use it directly.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints*(2+D)], dataset; destroyed in process
|
|
KX, KY - grid size
|
|
TileSize - tile size
|
|
InterfaceSize- interface size
|
|
NPoints - points count
|
|
D - number of components in vector-valued spline, D>=1
|
|
LSQRCnt - number of iterations, non-zero:
|
|
* LSQRCnt>0 means that specified amount of preconditioned
|
|
LSQR iterations will be performed to solve problem;
|
|
usually we need 2..5 its. Recommended option - best
|
|
convergence and stability/quality.
|
|
* LSQRCnt<0 means that instead of LSQR we use iterative
|
|
refinement on normal equations. Again, 2..5 its is enough.
|
|
Basis1 - basis spline, expected to be non-zero only at [-2,+2]
|
|
Z - possibly preallocated buffer for solution
|
|
Residuals - possibly preallocated buffer for residuals at dataset points
|
|
Rep - report structure; fields which are not set by this function
|
|
are left intact
|
|
TSS - total sum of squares; used to calculate R2
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
XY - destroyed in process
|
|
Z - array[KX*KY*D], filled by solution; KX*KY coefficients
|
|
corresponding to each of D dimensions are stored contiguously.
|
|
Rep - following fields are set:
|
|
* Rep.RMSError
|
|
* Rep.AvgError
|
|
* Rep.MaxError
|
|
* Rep.R2
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_fastddmfit(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t basecasex,
|
|
ae_int_t basecasey,
|
|
ae_int_t maxcoresize,
|
|
ae_int_t interfacesize,
|
|
ae_int_t nlayers,
|
|
double smoothing,
|
|
ae_int_t lsqrcnt,
|
|
spline1dinterpolant* basis1,
|
|
spline2dinterpolant* spline,
|
|
spline2dfitreport* rep,
|
|
double tss,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t nzwidth;
|
|
ae_int_t xew;
|
|
ae_int_t ntotallayers;
|
|
ae_int_t scaleidx;
|
|
ae_int_t scalexy;
|
|
double invscalexy;
|
|
ae_int_t kxcur;
|
|
ae_int_t kycur;
|
|
ae_int_t tilescount0;
|
|
ae_int_t tilescount1;
|
|
double v;
|
|
double rss;
|
|
ae_vector yraw;
|
|
ae_vector xyindex;
|
|
ae_vector tmp0;
|
|
ae_vector bufi;
|
|
spline2dfastddmbuf seed;
|
|
ae_shared_pool pool;
|
|
spline2dxdesignmatrix xdesignmatrix;
|
|
spline2dblockllsbuf blockllsbuf;
|
|
spline2dfitreport dummyrep;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&yraw, 0, sizeof(yraw));
|
|
memset(&xyindex, 0, sizeof(xyindex));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
memset(&bufi, 0, sizeof(bufi));
|
|
memset(&seed, 0, sizeof(seed));
|
|
memset(&pool, 0, sizeof(pool));
|
|
memset(&xdesignmatrix, 0, sizeof(xdesignmatrix));
|
|
memset(&blockllsbuf, 0, sizeof(blockllsbuf));
|
|
memset(&dummyrep, 0, sizeof(dummyrep));
|
|
ae_vector_init(&yraw, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&xyindex, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&tmp0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&bufi, 0, DT_INT, _state, ae_true);
|
|
_spline2dfastddmbuf_init(&seed, _state, ae_true);
|
|
ae_shared_pool_init(&pool, _state, ae_true);
|
|
_spline2dxdesignmatrix_init(&xdesignmatrix, _state, ae_true);
|
|
_spline2dblockllsbuf_init(&blockllsbuf, _state, ae_true);
|
|
_spline2dfitreport_init(&dummyrep, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Dataset metrics and integrity checks
|
|
*/
|
|
nzwidth = 4;
|
|
xew = 2+d;
|
|
ae_assert(maxcoresize>=2, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(interfacesize>=1, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(kx>=nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(ky>=nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
|
|
/*
|
|
* Verify consistency of the grid size (KX,KY) with basecase sizes.
|
|
* Determine full number of layers.
|
|
*/
|
|
ae_assert(basecasex<=maxcoresize, "Spline2DFit: integrity error", _state);
|
|
ae_assert(basecasey<=maxcoresize, "Spline2DFit: integrity error", _state);
|
|
ntotallayers = 1;
|
|
scalexy = 1;
|
|
kxcur = kx;
|
|
kycur = ky;
|
|
while(kxcur>basecasex+1&&kycur>basecasey+1)
|
|
{
|
|
ae_assert(kxcur%2==1, "Spline2DFit: integrity error", _state);
|
|
ae_assert(kycur%2==1, "Spline2DFit: integrity error", _state);
|
|
kxcur = (kxcur-1)/2+1;
|
|
kycur = (kycur-1)/2+1;
|
|
scalexy = scalexy*2;
|
|
inc(&ntotallayers, _state);
|
|
}
|
|
invscalexy = (double)1/(double)scalexy;
|
|
ae_assert((kxcur<=maxcoresize+1&&kxcur==basecasex+1)||kxcur%basecasex==1, "Spline2DFit: integrity error", _state);
|
|
ae_assert((kycur<=maxcoresize+1&&kycur==basecasey+1)||kycur%basecasey==1, "Spline2DFit: integrity error", _state);
|
|
ae_assert(kxcur==basecasex+1||kycur==basecasey+1, "Spline2DFit: integrity error", _state);
|
|
|
|
/*
|
|
* Initial scaling of dataset.
|
|
* Store original target values to YRaw.
|
|
*/
|
|
rvectorsetlengthatleast(&yraw, npoints*d, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
xy->ptr.p_double[xew*i+0] = xy->ptr.p_double[xew*i+0]*invscalexy;
|
|
xy->ptr.p_double[xew*i+1] = xy->ptr.p_double[xew*i+1]*invscalexy;
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
yraw.ptr.p_double[i*d+j] = xy->ptr.p_double[xew*i+2+j];
|
|
}
|
|
}
|
|
kxcur = (kx-1)/scalexy+1;
|
|
kycur = (ky-1)/scalexy+1;
|
|
|
|
/*
|
|
* Build initial dataset index; area is divided into (KXCur-1)*(KYCur-1)
|
|
* cells, with contiguous storage of points in the same cell.
|
|
* Iterate over different scales
|
|
*/
|
|
ae_shared_pool_set_seed(&pool, &seed, sizeof(seed), _spline2dfastddmbuf_init, _spline2dfastddmbuf_init_copy, _spline2dfastddmbuf_destroy, _state);
|
|
spline2d_reorderdatasetandbuildindex(xy, npoints, d, &yraw, d, kxcur, kycur, &xyindex, &bufi, _state);
|
|
for(scaleidx=ntotallayers-1; scaleidx>=0; scaleidx--)
|
|
{
|
|
if( (nlayers>0&&scaleidx<nlayers)||(nlayers<=0&&scaleidx<imax2(ntotallayers+nlayers, 1, _state)) )
|
|
{
|
|
|
|
/*
|
|
* Fit current layer
|
|
*/
|
|
ae_assert(kxcur%basecasex==1, "Spline2DFit: integrity error", _state);
|
|
ae_assert(kycur%basecasey==1, "Spline2DFit: integrity error", _state);
|
|
tilescount0 = kxcur/basecasex;
|
|
tilescount1 = kycur/basecasey;
|
|
spline2d_fastddmfitlayer(xy, d, scalexy, &xyindex, basecasex, 0, tilescount0, tilescount0, basecasey, 0, tilescount1, tilescount1, maxcoresize, interfacesize, lsqrcnt, spline2d_lambdaregfastddm+smoothing*ae_pow(spline2d_lambdadecay, (double)(scaleidx), _state), basis1, &pool, spline, _state);
|
|
|
|
/*
|
|
* Compute residuals and update XY
|
|
*/
|
|
spline2d_computeresidualsfromscratch(xy, &yraw, npoints, d, scalexy, spline, _state);
|
|
}
|
|
|
|
/*
|
|
* Move to the next level
|
|
*/
|
|
if( scaleidx!=0 )
|
|
{
|
|
|
|
/*
|
|
* Transform dataset (multply everything by 2.0) and refine grid.
|
|
*/
|
|
kxcur = 2*kxcur-1;
|
|
kycur = 2*kycur-1;
|
|
scalexy = scalexy/2;
|
|
invscalexy = (double)1/(double)scalexy;
|
|
spline2d_rescaledatasetandrefineindex(xy, npoints, d, &yraw, d, kxcur, kycur, &xyindex, &bufi, _state);
|
|
|
|
/*
|
|
* Clear temporaries from previous round.
|
|
*
|
|
* We have to do it because upper layer of the multilevel spline
|
|
* needs more memory then subsequent layers, and we want to free
|
|
* this memory as soon as possible.
|
|
*/
|
|
ae_shared_pool_clear_recycled(&pool, _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Post-check
|
|
*/
|
|
ae_assert(kxcur==kx, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(kycur==ky, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(scalexy==1, "Spline2DFit: integrity check failed", _state);
|
|
|
|
/*
|
|
* Report
|
|
*/
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rss = 0.0;
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
v = xy->ptr.p_double[i*xew+2+j];
|
|
rss = rss+v*v;
|
|
rep->rmserror = rep->rmserror+ae_sqr(v, _state);
|
|
rep->avgerror = rep->avgerror+ae_fabs(v, _state);
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v, _state), _state);
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/coalesce((double)(npoints*d), 1.0, _state), _state);
|
|
rep->avgerror = rep->avgerror/coalesce((double)(npoints*d), 1.0, _state);
|
|
rep->r2 = 1.0-rss/coalesce(tss, 1.0, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Recursive fitting function for FastDDM algorithm.
|
|
|
|
Works with KX*KY grid, with KX=BasecaseX*TilesCountX+1 and KY=BasecaseY*TilesCountY+1,
|
|
which is partitioned into TilesCountX*TilesCountY tiles, each having size
|
|
BasecaseX*BasecaseY.
|
|
|
|
This function processes tiles in range [TileX0,TileX1)x[TileY0,TileY1) and
|
|
recursively divides this range until we move down to single tile, which
|
|
is processed with BlockLLS solver.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_fastddmfitlayer(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t basecasex,
|
|
ae_int_t tilex0,
|
|
ae_int_t tilex1,
|
|
ae_int_t tilescountx,
|
|
ae_int_t basecasey,
|
|
ae_int_t tiley0,
|
|
ae_int_t tiley1,
|
|
ae_int_t tilescounty,
|
|
ae_int_t maxcoresize,
|
|
ae_int_t interfacesize,
|
|
ae_int_t lsqrcnt,
|
|
double lambdareg,
|
|
spline1dinterpolant* basis1,
|
|
ae_shared_pool* pool,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t kx;
|
|
ae_int_t ky;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t bfrad;
|
|
ae_int_t xa;
|
|
ae_int_t xb;
|
|
ae_int_t ya;
|
|
ae_int_t yb;
|
|
ae_int_t tile0;
|
|
ae_int_t tile1;
|
|
ae_int_t tilesize0;
|
|
ae_int_t tilesize1;
|
|
ae_int_t sfx;
|
|
ae_int_t sfy;
|
|
ae_int_t sfxy;
|
|
double dummytss;
|
|
double invscalexy;
|
|
ae_int_t cnt0;
|
|
ae_int_t cnt1;
|
|
ae_int_t offs;
|
|
double vs;
|
|
double vsx;
|
|
double vsy;
|
|
double vsxy;
|
|
spline2dfastddmbuf *buf;
|
|
ae_smart_ptr _buf;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_buf, 0, sizeof(_buf));
|
|
ae_smart_ptr_init(&_buf, (void**)&buf, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Dataset metrics and fast integrity checks;
|
|
* no code with side effects is allowed before parallel split.
|
|
*/
|
|
bfrad = 2;
|
|
invscalexy = (double)1/(double)scalexy;
|
|
kx = basecasex*tilescountx+1;
|
|
ky = basecasey*tilescounty+1;
|
|
|
|
/*
|
|
* Parallelism; because this function is intended for
|
|
* large-scale problems, we always try to:
|
|
* * invoke parallel execution mode
|
|
* * activate spawn support
|
|
*/
|
|
if( _trypexec_spline2d_fastddmfitlayer(xy,d,scalexy,xyindex,basecasex,tilex0,tilex1,tilescountx,basecasey,tiley0,tiley1,tilescounty,maxcoresize,interfacesize,lsqrcnt,lambdareg,basis1,pool,spline, _state) )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( imax2(tiley1-tiley0, tilex1-tilex0, _state)>=2 )
|
|
{
|
|
if( tiley1-tiley0>tilex1-tilex0 )
|
|
{
|
|
|
|
/*
|
|
* Split problem in Y dimension
|
|
*
|
|
* NOTE: recursive calls to FastDDMFitLayer() compute
|
|
* residuals in the inner cells defined by XYIndex[],
|
|
* but we still have to compute residuals for cells
|
|
* BETWEEN two recursive subdivisions of the task.
|
|
*/
|
|
tiledsplit(tiley1-tiley0, 1, &j0, &j1, _state);
|
|
spline2d_fastddmfitlayer(xy, d, scalexy, xyindex, basecasex, tilex0, tilex1, tilescountx, basecasey, tiley0, tiley0+j0, tilescounty, maxcoresize, interfacesize, lsqrcnt, lambdareg, basis1, pool, spline, _state);
|
|
spline2d_fastddmfitlayer(xy, d, scalexy, xyindex, basecasex, tilex0, tilex1, tilescountx, basecasey, tiley0+j0, tiley1, tilescounty, maxcoresize, interfacesize, lsqrcnt, lambdareg, basis1, pool, spline, _state);
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Split problem in X dimension
|
|
*
|
|
* NOTE: recursive calls to FastDDMFitLayer() compute
|
|
* residuals in the inner cells defined by XYIndex[],
|
|
* but we still have to compute residuals for cells
|
|
* BETWEEN two recursive subdivisions of the task.
|
|
*/
|
|
tiledsplit(tilex1-tilex0, 1, &j0, &j1, _state);
|
|
spline2d_fastddmfitlayer(xy, d, scalexy, xyindex, basecasex, tilex0, tilex0+j0, tilescountx, basecasey, tiley0, tiley1, tilescounty, maxcoresize, interfacesize, lsqrcnt, lambdareg, basis1, pool, spline, _state);
|
|
spline2d_fastddmfitlayer(xy, d, scalexy, xyindex, basecasex, tilex0+j0, tilex1, tilescountx, basecasey, tiley0, tiley1, tilescounty, maxcoresize, interfacesize, lsqrcnt, lambdareg, basis1, pool, spline, _state);
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_assert(tiley0==tiley1-1, "Spline2DFit.FastDDMFitLayer: integrity check failed", _state);
|
|
ae_assert(tilex0==tilex1-1, "Spline2DFit.FastDDMFitLayer: integrity check failed", _state);
|
|
tile1 = tiley0;
|
|
tile0 = tilex0;
|
|
|
|
/*
|
|
* Retrieve temporaries
|
|
*/
|
|
ae_shared_pool_retrieve(pool, &_buf, _state);
|
|
|
|
/*
|
|
* Analyze dataset
|
|
*/
|
|
xa = iboundval(tile0*basecasex-interfacesize, 0, kx, _state);
|
|
xb = iboundval((tile0+1)*basecasex+interfacesize, 0, kx, _state);
|
|
ya = iboundval(tile1*basecasey-interfacesize, 0, ky, _state);
|
|
yb = iboundval((tile1+1)*basecasey+interfacesize, 0, ky, _state);
|
|
tilesize0 = xb-xa;
|
|
tilesize1 = yb-ya;
|
|
|
|
/*
|
|
* Solve current chunk with BlockLLS
|
|
*/
|
|
dummytss = 1.0;
|
|
spline2d_xdesigngenerate(xy, xyindex, xa, xb, kx, ya, yb, ky, d, lambdareg, 0.0, basis1, &buf->xdesignmatrix, _state);
|
|
spline2d_blockllsfit(&buf->xdesignmatrix, lsqrcnt, &buf->tmpz, &buf->dummyrep, dummytss, &buf->blockllsbuf, _state);
|
|
buf->localmodel.d = d;
|
|
buf->localmodel.m = tilesize1;
|
|
buf->localmodel.n = tilesize0;
|
|
buf->localmodel.stype = -3;
|
|
rvectorsetlengthatleast(&buf->localmodel.x, tilesize0, _state);
|
|
rvectorsetlengthatleast(&buf->localmodel.y, tilesize1, _state);
|
|
rvectorsetlengthatleast(&buf->localmodel.f, tilesize0*tilesize1*d*4, _state);
|
|
for(i=0; i<=tilesize0-1; i++)
|
|
{
|
|
buf->localmodel.x.ptr.p_double[i] = (double)(xa+i);
|
|
}
|
|
for(i=0; i<=tilesize1-1; i++)
|
|
{
|
|
buf->localmodel.y.ptr.p_double[i] = (double)(ya+i);
|
|
}
|
|
for(i=0; i<=tilesize0*tilesize1*d*4-1; i++)
|
|
{
|
|
buf->localmodel.f.ptr.p_double[i] = 0.0;
|
|
}
|
|
spline2d_updatesplinetable(&buf->tmpz, tilesize0, tilesize1, d, basis1, bfrad, &buf->localmodel.f, tilesize1, tilesize0, 1, _state);
|
|
|
|
/*
|
|
* Transform local spline to original coordinates
|
|
*/
|
|
sfx = buf->localmodel.n*buf->localmodel.m*d;
|
|
sfy = 2*buf->localmodel.n*buf->localmodel.m*d;
|
|
sfxy = 3*buf->localmodel.n*buf->localmodel.m*d;
|
|
for(i=0; i<=tilesize0-1; i++)
|
|
{
|
|
buf->localmodel.x.ptr.p_double[i] = buf->localmodel.x.ptr.p_double[i]*scalexy;
|
|
}
|
|
for(i=0; i<=tilesize1-1; i++)
|
|
{
|
|
buf->localmodel.y.ptr.p_double[i] = buf->localmodel.y.ptr.p_double[i]*scalexy;
|
|
}
|
|
for(i=0; i<=tilesize0*tilesize1*d-1; i++)
|
|
{
|
|
buf->localmodel.f.ptr.p_double[sfx+i] = buf->localmodel.f.ptr.p_double[sfx+i]*invscalexy;
|
|
buf->localmodel.f.ptr.p_double[sfy+i] = buf->localmodel.f.ptr.p_double[sfy+i]*invscalexy;
|
|
buf->localmodel.f.ptr.p_double[sfxy+i] = buf->localmodel.f.ptr.p_double[sfxy+i]*(invscalexy*invscalexy);
|
|
}
|
|
|
|
/*
|
|
* Output results; for inner and topmost/leftmost tiles we output only BasecaseX*BasecaseY
|
|
* inner elements; for rightmost/bottom ones we also output one column/row of the interface
|
|
* part.
|
|
*
|
|
* Such complexity is explained by the fact that area size (by design) is not evenly divisible
|
|
* by the tile size; it is divisible with remainder=1, and we expect that interface size is
|
|
* at least 1, so we can fill the missing rightmost/bottom elements of Z by the interface
|
|
* values.
|
|
*/
|
|
ae_assert(interfacesize>=1, "Spline2DFit: integrity check failed", _state);
|
|
sfx = spline->n*spline->m*d;
|
|
sfy = 2*spline->n*spline->m*d;
|
|
sfxy = 3*spline->n*spline->m*d;
|
|
cnt0 = basecasex*scalexy;
|
|
cnt1 = basecasey*scalexy;
|
|
if( tile0==tilescountx-1 )
|
|
{
|
|
inc(&cnt0, _state);
|
|
}
|
|
if( tile1==tilescounty-1 )
|
|
{
|
|
inc(&cnt1, _state);
|
|
}
|
|
offs = d*(spline->n*tile1*basecasey*scalexy+tile0*basecasex*scalexy);
|
|
for(j1=0; j1<=cnt1-1; j1++)
|
|
{
|
|
for(j0=0; j0<=cnt0-1; j0++)
|
|
{
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
spline2ddiffvi(&buf->localmodel, (double)(tile0*basecasex*scalexy+j0), (double)(tile1*basecasey*scalexy+j1), j, &vs, &vsx, &vsy, &vsxy, _state);
|
|
spline->f.ptr.p_double[offs+d*(spline->n*j1+j0)+j] = spline->f.ptr.p_double[offs+d*(spline->n*j1+j0)+j]+vs;
|
|
spline->f.ptr.p_double[sfx+offs+d*(spline->n*j1+j0)+j] = spline->f.ptr.p_double[sfx+offs+d*(spline->n*j1+j0)+j]+vsx;
|
|
spline->f.ptr.p_double[sfy+offs+d*(spline->n*j1+j0)+j] = spline->f.ptr.p_double[sfy+offs+d*(spline->n*j1+j0)+j]+vsy;
|
|
spline->f.ptr.p_double[sfxy+offs+d*(spline->n*j1+j0)+j] = spline->f.ptr.p_double[sfxy+offs+d*(spline->n*j1+j0)+j]+vsxy;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Recycle temporaries
|
|
*/
|
|
ae_shared_pool_recycle(pool, &_buf, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_spline2d_fastddmfitlayer(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t basecasex,
|
|
ae_int_t tilex0,
|
|
ae_int_t tilex1,
|
|
ae_int_t tilescountx,
|
|
ae_int_t basecasey,
|
|
ae_int_t tiley0,
|
|
ae_int_t tiley1,
|
|
ae_int_t tilescounty,
|
|
ae_int_t maxcoresize,
|
|
ae_int_t interfacesize,
|
|
ae_int_t lsqrcnt,
|
|
double lambdareg,
|
|
spline1dinterpolant* basis1,
|
|
ae_shared_pool* pool,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs fitting with BlockLLS solver. Internal function,
|
|
never use it directly.
|
|
|
|
IMPORTANT: performance and memory requirements of this function are
|
|
asymmetric w.r.t. KX and KY: it has
|
|
* O(KY*KX^2) memory requirements
|
|
* O(KY*KX^3) running time
|
|
Thus, if you have large KY and small KX, simple transposition
|
|
of your dataset may give you great speedup.
|
|
|
|
INPUT PARAMETERS:
|
|
AV - sparse matrix, [ARows,KX*KY] in size. "Vertical" version
|
|
of design matrix, rows [0,NPoints) contain values of basis
|
|
functions at dataset points. Other rows are used for
|
|
nonlinearity penalty and other stuff like that.
|
|
AH - transpose(AV), "horizontal" version of AV
|
|
ARows - rows count
|
|
XY - array[NPoints*(2+D)], dataset
|
|
KX, KY - grid size
|
|
NPoints - points count
|
|
D - number of components in vector-valued spline, D>=1
|
|
LSQRCnt - number of iterations, non-zero:
|
|
* LSQRCnt>0 means that specified amount of preconditioned
|
|
LSQR iterations will be performed to solve problem;
|
|
usually we need 2..5 its. Recommended option - best
|
|
convergence and stability/quality.
|
|
* LSQRCnt<0 means that instead of LSQR we use iterative
|
|
refinement on normal equations. Again, 2..5 its is enough.
|
|
Z - possibly preallocated buffer for solution
|
|
Rep - report structure; fields which are not set by this function
|
|
are left intact
|
|
TSS - total sum of squares; used to calculate R2
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
XY - destroyed in process
|
|
Z - array[KX*KY*D], filled by solution; KX*KY coefficients
|
|
corresponding to each of D dimensions are stored contiguously.
|
|
Rep - following fields are set:
|
|
* Rep.RMSError
|
|
* Rep.AvgError
|
|
* Rep.MaxError
|
|
* Rep.R2
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_blockllsfit(spline2dxdesignmatrix* xdesign,
|
|
ae_int_t lsqrcnt,
|
|
/* Real */ ae_vector* z,
|
|
spline2dfitreport* rep,
|
|
double tss,
|
|
spline2dblockllsbuf* buf,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t blockbandwidth;
|
|
ae_int_t d;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
double lambdachol;
|
|
sreal mxata;
|
|
double v;
|
|
ae_int_t celloffset;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
double rss;
|
|
ae_int_t arows;
|
|
ae_int_t bw2;
|
|
ae_int_t kx;
|
|
ae_int_t ky;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&mxata, 0, sizeof(mxata));
|
|
_sreal_init(&mxata, _state, ae_true);
|
|
|
|
ae_assert(xdesign->blockwidth==4, "Spline2DFit: integrity check failed", _state);
|
|
blockbandwidth = 3;
|
|
d = xdesign->d;
|
|
arows = xdesign->nrows;
|
|
kx = xdesign->kx;
|
|
ky = xdesign->ky;
|
|
bw2 = xdesign->blockwidth*xdesign->blockwidth;
|
|
|
|
/*
|
|
* Initial values for Z/Residuals
|
|
*/
|
|
rvectorsetlengthatleast(z, kx*ky*d, _state);
|
|
for(i=0; i<=kx*ky*d-1; i++)
|
|
{
|
|
z->ptr.p_double[i] = (double)(0);
|
|
}
|
|
|
|
/*
|
|
* Create and factorize design matrix. Add regularizer if
|
|
* factorization failed (happens sometimes with zero
|
|
* smoothing and sparsely populated datasets).
|
|
*
|
|
* The algorithm below is refactoring of NaiveLLS algorithm,
|
|
* which uses sparsity properties and compressed block storage.
|
|
*
|
|
* Problem sparsity pattern results in block-band-diagonal
|
|
* matrix (block matrix with limited bandwidth, equal to 3
|
|
* for bicubic splines). Thus, we have KY*KY blocks, each
|
|
* of them is KX*KX in size. Design matrix is stored in
|
|
* large NROWS*KX matrix, with NROWS=(BlockBandwidth+1)*KY*KX.
|
|
*
|
|
* We use adaptation of block skyline storage format, with
|
|
* TOWERSIZE*KX skyline bands (towers) stored sequentially;
|
|
* here TOWERSIZE=(BlockBandwidth+1)*KX. So, we have KY
|
|
* "towers", stored one below other, in BlockATA matrix.
|
|
* Every "tower" is a sequence of BlockBandwidth+1 cells,
|
|
* each of them being KX*KX in size.
|
|
*/
|
|
lambdachol = spline2d_cholreg;
|
|
rmatrixsetlengthatleast(&buf->blockata, (blockbandwidth+1)*ky*kx, kx, _state);
|
|
for(;;)
|
|
{
|
|
|
|
/*
|
|
* Parallel generation of squared design matrix.
|
|
*/
|
|
spline2d_xdesignblockata(xdesign, &buf->blockata, &mxata.val, _state);
|
|
|
|
/*
|
|
* Regularization
|
|
*/
|
|
v = coalesce(mxata.val, 1.0, _state)*lambdachol;
|
|
for(i1=0; i1<=ky-1; i1++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, i1, i1, _state);
|
|
for(i0=0; i0<=kx-1; i0++)
|
|
{
|
|
buf->blockata.ptr.pp_double[celloffset+i0][i0] = buf->blockata.ptr.pp_double[celloffset+i0][i0]+v;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Try Cholesky factorization.
|
|
*/
|
|
if( !spline2d_blockllscholesky(&buf->blockata, kx, ky, &buf->trsmbuf2, &buf->cholbuf2, &buf->cholbuf1, _state) )
|
|
{
|
|
|
|
/*
|
|
* Factorization failed, increase regularizer and repeat
|
|
*/
|
|
lambdachol = coalesce(10*lambdachol, 1.0E-12, _state);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Solve
|
|
*/
|
|
rss = 0.0;
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
ae_assert(lsqrcnt>0, "Spline2DFit: integrity failure", _state);
|
|
rvectorsetlengthatleast(&buf->tmp0, arows, _state);
|
|
rvectorsetlengthatleast(&buf->tmp1, kx*ky, _state);
|
|
linlsqrcreatebuf(arows, kx*ky, &buf->solver, _state);
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
|
|
/*
|
|
* Preconditioned LSQR:
|
|
*
|
|
* use Cholesky factor U of squared design matrix A'*A to
|
|
* transform min|A*x-b| to min|[A*inv(U)]*y-b| with y=U*x.
|
|
*
|
|
* Preconditioned problem is solved with LSQR solver, which
|
|
* gives superior results than normal equations.
|
|
*/
|
|
for(i=0; i<=arows-1; i++)
|
|
{
|
|
if( i<xdesign->npoints )
|
|
{
|
|
buf->tmp0.ptr.p_double[i] = xdesign->vals.ptr.pp_double[i][bw2+j];
|
|
}
|
|
else
|
|
{
|
|
buf->tmp0.ptr.p_double[i] = 0.0;
|
|
}
|
|
}
|
|
linlsqrrestart(&buf->solver, _state);
|
|
linlsqrsetb(&buf->solver, &buf->tmp0, _state);
|
|
linlsqrsetcond(&buf->solver, 1.0E-14, 1.0E-14, lsqrcnt, _state);
|
|
while(linlsqriteration(&buf->solver, _state))
|
|
{
|
|
if( buf->solver.needmv )
|
|
{
|
|
|
|
/*
|
|
* Use Cholesky factorization of the system matrix
|
|
* as preconditioner: solve TRSV(U,Solver.X)
|
|
*/
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
buf->tmp1.ptr.p_double[i] = buf->solver.x.ptr.p_double[i];
|
|
}
|
|
spline2d_blockllstrsv(&buf->blockata, kx, ky, ae_false, &buf->tmp1, _state);
|
|
|
|
/*
|
|
* After preconditioning is done, multiply by A
|
|
*/
|
|
spline2d_xdesignmv(xdesign, &buf->tmp1, &buf->solver.mv, _state);
|
|
}
|
|
if( buf->solver.needmtv )
|
|
{
|
|
|
|
/*
|
|
* Multiply by design matrix A
|
|
*/
|
|
spline2d_xdesignmtv(xdesign, &buf->solver.x, &buf->solver.mtv, _state);
|
|
|
|
/*
|
|
* Multiply by preconditioner: solve TRSV(U',A*Solver.X)
|
|
*/
|
|
spline2d_blockllstrsv(&buf->blockata, kx, ky, ae_true, &buf->solver.mtv, _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get results and post-multiply by preconditioner to get
|
|
* original variables.
|
|
*/
|
|
linlsqrresults(&buf->solver, &buf->tmp1, &buf->solverrep, _state);
|
|
spline2d_blockllstrsv(&buf->blockata, kx, ky, ae_false, &buf->tmp1, _state);
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
z->ptr.p_double[kx*ky*j+i] = buf->tmp1.ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Calculate model values
|
|
*/
|
|
spline2d_xdesignmv(xdesign, &buf->tmp1, &buf->tmp0, _state);
|
|
for(i=0; i<=xdesign->npoints-1; i++)
|
|
{
|
|
v = xdesign->vals.ptr.pp_double[i][bw2+j]-buf->tmp0.ptr.p_double[i];
|
|
rss = rss+v*v;
|
|
rep->rmserror = rep->rmserror+ae_sqr(v, _state);
|
|
rep->avgerror = rep->avgerror+ae_fabs(v, _state);
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v, _state), _state);
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/coalesce((double)(xdesign->npoints*d), 1.0, _state), _state);
|
|
rep->avgerror = rep->avgerror/coalesce((double)(xdesign->npoints*d), 1.0, _state);
|
|
rep->r2 = 1.0-rss/coalesce(tss, 1.0, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs fitting with NaiveLLS solver. Internal function,
|
|
never use it directly.
|
|
|
|
INPUT PARAMETERS:
|
|
AV - sparse matrix, [ARows,KX*KY] in size. "Vertical" version
|
|
of design matrix, rows [0,NPoints] contain values of basis
|
|
functions at dataset points. Other rows are used for
|
|
nonlinearity penalty and other stuff like that.
|
|
AH - transpose(AV), "horizontal" version of AV
|
|
ARows - rows count
|
|
XY - array[NPoints*(2+D)], dataset
|
|
KX, KY - grid size
|
|
NPoints - points count
|
|
D - number of components in vector-valued spline, D>=1
|
|
LSQRCnt - number of iterations, non-zero:
|
|
* LSQRCnt>0 means that specified amount of preconditioned
|
|
LSQR iterations will be performed to solve problem;
|
|
usually we need 2..5 its. Recommended option - best
|
|
convergence and stability/quality.
|
|
* LSQRCnt<0 means that instead of LSQR we use iterative
|
|
refinement on normal equations. Again, 2..5 its is enough.
|
|
Z - possibly preallocated buffer for solution
|
|
Rep - report structure; fields which are not set by this function
|
|
are left intact
|
|
TSS - total sum of squares; used to calculate R2
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
XY - destroyed in process
|
|
Z - array[KX*KY*D], filled by solution; KX*KY coefficients
|
|
corresponding to each of D dimensions are stored contiguously.
|
|
Rep - following fields are set:
|
|
* Rep.RMSError
|
|
* Rep.AvgError
|
|
* Rep.MaxError
|
|
* Rep.R2
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_naivellsfit(sparsematrix* av,
|
|
sparsematrix* ah,
|
|
ae_int_t arows,
|
|
/* Real */ ae_vector* xy,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t lsqrcnt,
|
|
/* Real */ ae_vector* z,
|
|
spline2dfitreport* rep,
|
|
double tss,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t ew;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
double v;
|
|
ae_int_t blockbandwidth;
|
|
double lambdareg;
|
|
ae_int_t srci;
|
|
ae_int_t srcj;
|
|
ae_int_t idxi;
|
|
ae_int_t idxj;
|
|
ae_int_t endi;
|
|
ae_int_t endj;
|
|
ae_int_t rfsidx;
|
|
ae_matrix ata;
|
|
ae_vector tmp0;
|
|
ae_vector tmp1;
|
|
double mxata;
|
|
linlsqrstate solver;
|
|
linlsqrreport solverrep;
|
|
double rss;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&ata, 0, sizeof(ata));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
memset(&tmp1, 0, sizeof(tmp1));
|
|
memset(&solver, 0, sizeof(solver));
|
|
memset(&solverrep, 0, sizeof(solverrep));
|
|
ae_matrix_init(&ata, 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);
|
|
_linlsqrstate_init(&solver, _state, ae_true);
|
|
_linlsqrreport_init(&solverrep, _state, ae_true);
|
|
|
|
blockbandwidth = 3;
|
|
ew = 2+d;
|
|
|
|
/*
|
|
* Initial values for Z/Residuals
|
|
*/
|
|
rvectorsetlengthatleast(z, kx*ky*d, _state);
|
|
for(i=0; i<=kx*ky*d-1; i++)
|
|
{
|
|
z->ptr.p_double[i] = (double)(0);
|
|
}
|
|
|
|
/*
|
|
* Create and factorize design matrix.
|
|
*
|
|
* Add regularizer if factorization failed (happens sometimes
|
|
* with zero smoothing and sparsely populated datasets).
|
|
*/
|
|
lambdareg = spline2d_cholreg;
|
|
rmatrixsetlengthatleast(&ata, kx*ky, kx*ky, _state);
|
|
for(;;)
|
|
{
|
|
mxata = 0.0;
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
for(j=i; j<=kx*ky-1; j++)
|
|
{
|
|
|
|
/*
|
|
* Initialize by zero
|
|
*/
|
|
ata.ptr.pp_double[i][j] = (double)(0);
|
|
|
|
/*
|
|
* Determine grid nodes corresponding to I and J;
|
|
* skip if too far away
|
|
*/
|
|
i0 = i%kx;
|
|
i1 = i/kx;
|
|
j0 = j%kx;
|
|
j1 = j/kx;
|
|
if( ae_iabs(i0-j0, _state)>blockbandwidth||ae_iabs(i1-j1, _state)>blockbandwidth )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Nodes are close enough, calculate product of columns I and J of A.
|
|
*/
|
|
v = (double)(0);
|
|
srci = ah->ridx.ptr.p_int[i];
|
|
srcj = ah->ridx.ptr.p_int[j];
|
|
endi = ah->ridx.ptr.p_int[i+1];
|
|
endj = ah->ridx.ptr.p_int[j+1];
|
|
for(;;)
|
|
{
|
|
if( srci>=endi||srcj>=endj )
|
|
{
|
|
break;
|
|
}
|
|
idxi = ah->idx.ptr.p_int[srci];
|
|
idxj = ah->idx.ptr.p_int[srcj];
|
|
if( idxi==idxj )
|
|
{
|
|
v = v+ah->vals.ptr.p_double[srci]*ah->vals.ptr.p_double[srcj];
|
|
srci = srci+1;
|
|
srcj = srcj+1;
|
|
continue;
|
|
}
|
|
if( idxi<idxj )
|
|
{
|
|
srci = srci+1;
|
|
}
|
|
else
|
|
{
|
|
srcj = srcj+1;
|
|
}
|
|
}
|
|
ata.ptr.pp_double[i][j] = v;
|
|
mxata = ae_maxreal(mxata, ae_fabs(v, _state), _state);
|
|
}
|
|
}
|
|
v = coalesce(mxata, 1.0, _state)*lambdareg;
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
ata.ptr.pp_double[i][i] = ata.ptr.pp_double[i][i]+v;
|
|
}
|
|
if( spdmatrixcholesky(&ata, kx*ky, ae_true, _state) )
|
|
{
|
|
|
|
/*
|
|
* Success!
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Factorization failed, increase regularizer and repeat
|
|
*/
|
|
lambdareg = coalesce(10*lambdareg, 1.0E-12, _state);
|
|
}
|
|
|
|
/*
|
|
* Solve
|
|
*
|
|
* NOTE: we expect that Z is zero-filled, and we treat it
|
|
* like initial approximation to solution.
|
|
*/
|
|
rvectorsetlengthatleast(&tmp0, arows, _state);
|
|
rvectorsetlengthatleast(&tmp1, kx*ky, _state);
|
|
if( lsqrcnt>0 )
|
|
{
|
|
linlsqrcreate(arows, kx*ky, &solver, _state);
|
|
}
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
ae_assert(lsqrcnt!=0, "Spline2DFit: integrity failure", _state);
|
|
if( lsqrcnt>0 )
|
|
{
|
|
|
|
/*
|
|
* Preconditioned LSQR:
|
|
*
|
|
* use Cholesky factor U of squared design matrix A'*A to
|
|
* transform min|A*x-b| to min|[A*inv(U)]*y-b| with y=U*x.
|
|
*
|
|
* Preconditioned problem is solved with LSQR solver, which
|
|
* gives superior results than normal equations.
|
|
*/
|
|
linlsqrcreate(arows, kx*ky, &solver, _state);
|
|
for(i=0; i<=arows-1; i++)
|
|
{
|
|
if( i<npoints )
|
|
{
|
|
tmp0.ptr.p_double[i] = xy->ptr.p_double[i*ew+2+j];
|
|
}
|
|
else
|
|
{
|
|
tmp0.ptr.p_double[i] = 0.0;
|
|
}
|
|
}
|
|
linlsqrsetb(&solver, &tmp0, _state);
|
|
linlsqrsetcond(&solver, 1.0E-14, 1.0E-14, lsqrcnt, _state);
|
|
while(linlsqriteration(&solver, _state))
|
|
{
|
|
if( solver.needmv )
|
|
{
|
|
|
|
/*
|
|
* Use Cholesky factorization of the system matrix
|
|
* as preconditioner: solve TRSV(U,Solver.X)
|
|
*/
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
tmp1.ptr.p_double[i] = solver.x.ptr.p_double[i];
|
|
}
|
|
rmatrixtrsv(kx*ky, &ata, 0, 0, ae_true, ae_false, 0, &tmp1, 0, _state);
|
|
|
|
/*
|
|
* After preconditioning is done, multiply by A
|
|
*/
|
|
sparsemv(av, &tmp1, &solver.mv, _state);
|
|
}
|
|
if( solver.needmtv )
|
|
{
|
|
|
|
/*
|
|
* Multiply by design matrix A
|
|
*/
|
|
sparsemv(ah, &solver.x, &solver.mtv, _state);
|
|
|
|
/*
|
|
* Multiply by preconditioner: solve TRSV(U',A*Solver.X)
|
|
*/
|
|
rmatrixtrsv(kx*ky, &ata, 0, 0, ae_true, ae_false, 1, &solver.mtv, 0, _state);
|
|
}
|
|
}
|
|
linlsqrresults(&solver, &tmp1, &solverrep, _state);
|
|
rmatrixtrsv(kx*ky, &ata, 0, 0, ae_true, ae_false, 0, &tmp1, 0, _state);
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
z->ptr.p_double[kx*ky*j+i] = tmp1.ptr.p_double[i];
|
|
}
|
|
|
|
/*
|
|
* Calculate model values
|
|
*/
|
|
sparsemv(av, &tmp1, &tmp0, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
xy->ptr.p_double[i*ew+2+j] = xy->ptr.p_double[i*ew+2+j]-tmp0.ptr.p_double[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Iterative refinement, inferior to LSQR
|
|
*
|
|
* For each dimension D:
|
|
* * fetch current estimate for solution from Z to Tmp1
|
|
* * calculate residual r for current estimate, store in Tmp0
|
|
* * calculate product of residual and design matrix A'*r, store it in Tmp1
|
|
* * Cholesky solver
|
|
* * update current estimate
|
|
*/
|
|
for(rfsidx=1; rfsidx<=-lsqrcnt; rfsidx++)
|
|
{
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
tmp1.ptr.p_double[i] = z->ptr.p_double[kx*ky*j+i];
|
|
}
|
|
sparsemv(av, &tmp1, &tmp0, _state);
|
|
for(i=0; i<=arows-1; i++)
|
|
{
|
|
if( i<npoints )
|
|
{
|
|
v = xy->ptr.p_double[i*ew+2+j];
|
|
}
|
|
else
|
|
{
|
|
v = (double)(0);
|
|
}
|
|
tmp0.ptr.p_double[i] = v-tmp0.ptr.p_double[i];
|
|
}
|
|
sparsemv(ah, &tmp0, &tmp1, _state);
|
|
rmatrixtrsv(kx*ky, &ata, 0, 0, ae_true, ae_false, 1, &tmp1, 0, _state);
|
|
rmatrixtrsv(kx*ky, &ata, 0, 0, ae_true, ae_false, 0, &tmp1, 0, _state);
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
z->ptr.p_double[kx*ky*j+i] = z->ptr.p_double[kx*ky*j+i]+tmp1.ptr.p_double[i];
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate model values
|
|
*/
|
|
for(i=0; i<=kx*ky-1; i++)
|
|
{
|
|
tmp1.ptr.p_double[i] = z->ptr.p_double[kx*ky*j+i];
|
|
}
|
|
sparsemv(av, &tmp1, &tmp0, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
xy->ptr.p_double[i*ew+2+j] = xy->ptr.p_double[i*ew+2+j]-tmp0.ptr.p_double[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate report
|
|
*/
|
|
rep->rmserror = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
rss = 0.0;
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
v = xy->ptr.p_double[i*ew+2+j];
|
|
rss = rss+v*v;
|
|
rep->rmserror = rep->rmserror+ae_sqr(v, _state);
|
|
rep->avgerror = rep->avgerror+ae_fabs(v, _state);
|
|
rep->maxerror = ae_maxreal(rep->maxerror, ae_fabs(v, _state), _state);
|
|
}
|
|
}
|
|
rep->rmserror = ae_sqrt(rep->rmserror/coalesce((double)(npoints*d), 1.0, _state), _state);
|
|
rep->avgerror = rep->avgerror/coalesce((double)(npoints*d), 1.0, _state);
|
|
rep->r2 = 1.0-rss/coalesce(tss, 1.0, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This is convenience function for band block storage format; it returns
|
|
offset of KX*KX-sized block (I,J) in a compressed 2D array.
|
|
|
|
For specific offset=OFFSET,
|
|
block (I,J) will be stored in entries BlockMatrix[OFFSET:OFFSET+KX-1,0:KX-1]
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static ae_int_t spline2d_getcelloffset(ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t blockbandwidth,
|
|
ae_int_t i,
|
|
ae_int_t j,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t result;
|
|
|
|
|
|
ae_assert(i>=0&&i<ky, "Spline2DFit: GetCellOffset() integrity error", _state);
|
|
ae_assert(j>=0&&j<ky, "Spline2DFit: GetCellOffset() integrity error", _state);
|
|
ae_assert(j>=i&&j<=i+blockbandwidth, "Spline2DFit: GetCellOffset() integrity error", _state);
|
|
result = j*(blockbandwidth+1)*kx;
|
|
result = result+(blockbandwidth-(j-i))*kx;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This is convenience function for band block storage format; it copies
|
|
cell (I,J) from compressed format to uncompressed general matrix, at desired
|
|
position.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_copycellto(ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t blockbandwidth,
|
|
/* Real */ ae_matrix* blockata,
|
|
ae_int_t i,
|
|
ae_int_t j,
|
|
/* Real */ ae_matrix* dst,
|
|
ae_int_t dst0,
|
|
ae_int_t dst1,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t celloffset;
|
|
ae_int_t idx0;
|
|
ae_int_t idx1;
|
|
|
|
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, i, j, _state);
|
|
for(idx0=0; idx0<=kx-1; idx0++)
|
|
{
|
|
for(idx1=0; idx1<=kx-1; idx1++)
|
|
{
|
|
dst->ptr.pp_double[dst0+idx0][dst1+idx1] = blockata->ptr.pp_double[celloffset+idx0][idx1];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This is convenience function for band block storage format; it
|
|
truncates all elements of cell (I,J) which are less than Eps in magnitude.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_flushtozerocell(ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_int_t blockbandwidth,
|
|
/* Real */ ae_matrix* blockata,
|
|
ae_int_t i,
|
|
ae_int_t j,
|
|
double eps,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t celloffset;
|
|
ae_int_t idx0;
|
|
ae_int_t idx1;
|
|
double eps2;
|
|
double v;
|
|
|
|
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, i, j, _state);
|
|
eps2 = eps*eps;
|
|
for(idx0=0; idx0<=kx-1; idx0++)
|
|
{
|
|
for(idx1=0; idx1<=kx-1; idx1++)
|
|
{
|
|
v = blockata->ptr.pp_double[celloffset+idx0][idx1];
|
|
if( v*v<eps2 )
|
|
{
|
|
blockata->ptr.pp_double[celloffset+idx0][idx1] = (double)(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function generates squared design matrix stored in block band format.
|
|
|
|
We use adaptation of block skyline storage format, with
|
|
TOWERSIZE*KX skyline bands (towers) stored sequentially;
|
|
here TOWERSIZE=(BlockBandwidth+1)*KX. So, we have KY
|
|
"towers", stored one below other, in BlockATA matrix.
|
|
Every "tower" is a sequence of BlockBandwidth+1 cells,
|
|
each of them being KX*KX in size.
|
|
|
|
INPUT PARAMETERS:
|
|
AH - sparse matrix, [KX*KY,ARows] in size. "Horizontal" version
|
|
of design matrix, cols [0,NPoints] contain values of basis
|
|
functions at dataset points. Other cols are used for
|
|
nonlinearity penalty and other stuff like that.
|
|
KY0, KY1- subset of output matrix bands to process; on entry it MUST
|
|
be set to 0 and KY respectively.
|
|
KX, KY - grid size
|
|
BlockATA- array[KY*(BlockBandwidth+1)*KX,KX], preallocated storage
|
|
for output matrix in compressed block band format
|
|
MXATA - on entry MUST be zero
|
|
|
|
OUTPUT PARAMETERS:
|
|
BlockATA- AH*AH', stored in compressed block band format
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_blockllsgenerateata(sparsematrix* ah,
|
|
ae_int_t ky0,
|
|
ae_int_t ky1,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Real */ ae_matrix* blockata,
|
|
sreal* mxata,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t blockbandwidth;
|
|
double avgrowlen;
|
|
double cellcost;
|
|
double totalcost;
|
|
sreal tmpmxata;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t celloffset;
|
|
double v;
|
|
ae_int_t srci;
|
|
ae_int_t srcj;
|
|
ae_int_t idxi;
|
|
ae_int_t idxj;
|
|
ae_int_t endi;
|
|
ae_int_t endj;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&tmpmxata, 0, sizeof(tmpmxata));
|
|
_sreal_init(&tmpmxata, _state, ae_true);
|
|
|
|
ae_assert(ae_fp_greater_eq(mxata->val,(double)(0)), "BlockLLSGenerateATA: integrity check failed", _state);
|
|
blockbandwidth = 3;
|
|
|
|
/*
|
|
* Determine problem cost, perform recursive subdivision
|
|
* (with optional parallelization)
|
|
*/
|
|
avgrowlen = (double)ah->ridx.ptr.p_int[kx*ky]/(double)(kx*ky);
|
|
cellcost = rmul3((double)(kx), (double)(1+2*blockbandwidth), avgrowlen, _state);
|
|
totalcost = rmul3((double)(ky1-ky0), (double)(1+2*blockbandwidth), cellcost, _state);
|
|
if( ky1-ky0>=2&&ae_fp_greater(totalcost,smpactivationlevel(_state)) )
|
|
{
|
|
if( _trypexec_spline2d_blockllsgenerateata(ah,ky0,ky1,kx,ky,blockata,mxata, _state) )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
if( ky1-ky0>=2 )
|
|
{
|
|
|
|
/*
|
|
* Split X: X*A = (X1 X2)^T*A
|
|
*/
|
|
j = (ky1-ky0)/2;
|
|
spline2d_blockllsgenerateata(ah, ky0, ky0+j, kx, ky, blockata, &tmpmxata, _state);
|
|
spline2d_blockllsgenerateata(ah, ky0+j, ky1, kx, ky, blockata, mxata, _state);
|
|
mxata->val = ae_maxreal(mxata->val, tmpmxata.val, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Splitting in Y-dimension is done, fill I1-th "tower"
|
|
*/
|
|
ae_assert(ky1==ky0+1, "BlockLLSGenerateATA: integrity check failed", _state);
|
|
i1 = ky0;
|
|
for(j1=i1; j1<=ae_minint(ky-1, i1+blockbandwidth, _state); j1++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, i1, j1, _state);
|
|
|
|
/*
|
|
* Clear cell (I1,J1)
|
|
*/
|
|
for(i0=0; i0<=kx-1; i0++)
|
|
{
|
|
for(j0=0; j0<=kx-1; j0++)
|
|
{
|
|
blockata->ptr.pp_double[celloffset+i0][j0] = 0.0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize cell internals
|
|
*/
|
|
for(i0=0; i0<=kx-1; i0++)
|
|
{
|
|
for(j0=0; j0<=kx-1; j0++)
|
|
{
|
|
if( ae_iabs(i0-j0, _state)<=blockbandwidth )
|
|
{
|
|
|
|
/*
|
|
* Nodes are close enough, calculate product of columns I and J of A.
|
|
*/
|
|
v = (double)(0);
|
|
i = i1*kx+i0;
|
|
j = j1*kx+j0;
|
|
srci = ah->ridx.ptr.p_int[i];
|
|
srcj = ah->ridx.ptr.p_int[j];
|
|
endi = ah->ridx.ptr.p_int[i+1];
|
|
endj = ah->ridx.ptr.p_int[j+1];
|
|
for(;;)
|
|
{
|
|
if( srci>=endi||srcj>=endj )
|
|
{
|
|
break;
|
|
}
|
|
idxi = ah->idx.ptr.p_int[srci];
|
|
idxj = ah->idx.ptr.p_int[srcj];
|
|
if( idxi==idxj )
|
|
{
|
|
v = v+ah->vals.ptr.p_double[srci]*ah->vals.ptr.p_double[srcj];
|
|
srci = srci+1;
|
|
srcj = srcj+1;
|
|
continue;
|
|
}
|
|
if( idxi<idxj )
|
|
{
|
|
srci = srci+1;
|
|
}
|
|
else
|
|
{
|
|
srcj = srcj+1;
|
|
}
|
|
}
|
|
blockata->ptr.pp_double[celloffset+i0][j0] = v;
|
|
mxata->val = ae_maxreal(mxata->val, ae_fabs(v, _state), _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_spline2d_blockllsgenerateata(sparsematrix* ah,
|
|
ae_int_t ky0,
|
|
ae_int_t ky1,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Real */ ae_matrix* blockata,
|
|
sreal* mxata,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs Cholesky decomposition of squared design matrix
|
|
stored in block band format.
|
|
|
|
INPUT PARAMETERS:
|
|
BlockATA - array[KY*(BlockBandwidth+1)*KX,KX], matrix in compressed
|
|
block band format
|
|
KX, KY - grid size
|
|
TrsmBuf2,
|
|
CholBuf2,
|
|
CholBuf1 - buffers; reused by this function on subsequent calls,
|
|
automatically preallocated on the first call
|
|
|
|
OUTPUT PARAMETERS:
|
|
BlockATA- Cholesky factor, in compressed block band format
|
|
|
|
Result:
|
|
True on success, False on Cholesky failure
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static ae_bool spline2d_blockllscholesky(/* Real */ ae_matrix* blockata,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Real */ ae_matrix* trsmbuf2,
|
|
/* Real */ ae_matrix* cholbuf2,
|
|
/* Real */ ae_vector* cholbuf1,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t blockbandwidth;
|
|
ae_int_t blockidx;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t celloffset;
|
|
ae_int_t celloffset1;
|
|
ae_bool result;
|
|
|
|
|
|
blockbandwidth = 3;
|
|
rmatrixsetlengthatleast(trsmbuf2, (blockbandwidth+1)*kx, (blockbandwidth+1)*kx, _state);
|
|
rmatrixsetlengthatleast(cholbuf2, kx, kx, _state);
|
|
rvectorsetlengthatleast(cholbuf1, kx, _state);
|
|
result = ae_true;
|
|
for(blockidx=0; blockidx<=ky-1; blockidx++)
|
|
{
|
|
|
|
/*
|
|
* TRSM for TRAIL*TRAIL block matrix before current cell;
|
|
* here TRAIL=MinInt(BlockIdx,BlockBandwidth).
|
|
*/
|
|
for(i=0; i<=ae_minint(blockidx, blockbandwidth, _state)-1; i++)
|
|
{
|
|
for(j=i; j<=ae_minint(blockidx, blockbandwidth, _state)-1; j++)
|
|
{
|
|
spline2d_copycellto(kx, ky, blockbandwidth, blockata, ae_maxint(blockidx-blockbandwidth, 0, _state)+i, ae_maxint(blockidx-blockbandwidth, 0, _state)+j, trsmbuf2, i*kx, j*kx, _state);
|
|
}
|
|
}
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, ae_maxint(blockidx-blockbandwidth, 0, _state), blockidx, _state);
|
|
rmatrixlefttrsm(ae_minint(blockidx, blockbandwidth, _state)*kx, kx, trsmbuf2, 0, 0, ae_true, ae_false, 1, blockata, celloffset, 0, _state);
|
|
|
|
/*
|
|
* SYRK for diagonal cell: MaxInt(BlockIdx-BlockBandwidth,0)
|
|
* cells above diagonal one are used for update.
|
|
*/
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, ae_maxint(blockidx-blockbandwidth, 0, _state), blockidx, _state);
|
|
celloffset1 = spline2d_getcelloffset(kx, ky, blockbandwidth, blockidx, blockidx, _state);
|
|
rmatrixsyrk(kx, ae_minint(blockidx, blockbandwidth, _state)*kx, -1.0, blockata, celloffset, 0, 1, 1.0, blockata, celloffset1, 0, ae_true, _state);
|
|
|
|
/*
|
|
* Factorize diagonal cell
|
|
*/
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, blockidx, blockidx, _state);
|
|
rmatrixcopy(kx, kx, blockata, celloffset, 0, cholbuf2, 0, 0, _state);
|
|
if( !spdmatrixcholeskyrec(cholbuf2, 0, kx, ae_true, cholbuf1, _state) )
|
|
{
|
|
result = ae_false;
|
|
return result;
|
|
}
|
|
rmatrixcopy(kx, kx, cholbuf2, 0, 0, blockata, celloffset, 0, _state);
|
|
|
|
/*
|
|
* PERFORMANCE TWEAK: drop nearly-denormals from last "tower".
|
|
*
|
|
* Sparse matrices like these may produce denormal numbers on
|
|
* sparse datasets, with significant (10x!) performance penalty
|
|
* on Intel chips. In order to avoid it, we manually truncate
|
|
* small enough numbers.
|
|
*
|
|
* We use 1.0E-50 as clipping level (not really denormal, but
|
|
* such small numbers are not actually important anyway).
|
|
*/
|
|
for(i=ae_maxint(blockidx-blockbandwidth, 0, _state); i<=blockidx; i++)
|
|
{
|
|
spline2d_flushtozerocell(kx, ky, blockbandwidth, blockata, i, blockidx, 1.0E-50, _state);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs TRSV on upper triangular Cholesky factor U, solving
|
|
either U*x=b or U'*x=b.
|
|
|
|
INPUT PARAMETERS:
|
|
BlockATA - array[KY*(BlockBandwidth+1)*KX,KX], matrix U
|
|
in compressed block band format
|
|
KX, KY - grid size
|
|
TransU - whether to transpose U or not
|
|
B - array[KX*KY], on entry - stores right part B
|
|
|
|
OUTPUT PARAMETERS:
|
|
B - replaced by X
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_blockllstrsv(/* Real */ ae_matrix* blockata,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
ae_bool transu,
|
|
/* Real */ ae_vector* b,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t blockbandwidth;
|
|
ae_int_t blockidx;
|
|
ae_int_t blockidx1;
|
|
ae_int_t celloffset;
|
|
|
|
|
|
blockbandwidth = 3;
|
|
if( !transu )
|
|
{
|
|
|
|
/*
|
|
* Solve U*x=b
|
|
*/
|
|
for(blockidx=ky-1; blockidx>=0; blockidx--)
|
|
{
|
|
for(blockidx1=1; blockidx1<=ae_minint(ky-(blockidx+1), blockbandwidth, _state); blockidx1++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, blockidx, blockidx+blockidx1, _state);
|
|
rmatrixgemv(kx, kx, -1.0, blockata, celloffset, 0, 0, b, (blockidx+blockidx1)*kx, 1.0, b, blockidx*kx, _state);
|
|
}
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, blockidx, blockidx, _state);
|
|
rmatrixtrsv(kx, blockata, celloffset, 0, ae_true, ae_false, 0, b, blockidx*kx, _state);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Solve U'*x=b
|
|
*/
|
|
for(blockidx=0; blockidx<=ky-1; blockidx++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, blockidx, blockidx, _state);
|
|
rmatrixtrsv(kx, blockata, celloffset, 0, ae_true, ae_false, 1, b, blockidx*kx, _state);
|
|
for(blockidx1=1; blockidx1<=ae_minint(ky-(blockidx+1), blockbandwidth, _state); blockidx1++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, blockidx, blockidx+blockidx1, _state);
|
|
rmatrixgemv(kx, kx, -1.0, blockata, celloffset, 0, 1, b, blockidx*kx, 1.0, b, (blockidx+blockidx1)*kx, _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function computes residuals for dataset XY[], using array of original
|
|
values YRaw[], and loads residuals to XY.
|
|
|
|
Processing is performed in parallel manner.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_computeresidualsfromscratch(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
srealarray seed;
|
|
ae_shared_pool pool;
|
|
ae_int_t chunksize;
|
|
double pointcost;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&seed, 0, sizeof(seed));
|
|
memset(&pool, 0, sizeof(pool));
|
|
_srealarray_init(&seed, _state, ae_true);
|
|
ae_shared_pool_init(&pool, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Setting up
|
|
*/
|
|
chunksize = 1000;
|
|
pointcost = 100.0;
|
|
if( ae_fp_greater(npoints*pointcost,smpactivationlevel(_state)) )
|
|
{
|
|
if( _trypexec_spline2d_computeresidualsfromscratch(xy,yraw,npoints,d,scalexy,spline, _state) )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
ae_shared_pool_set_seed(&pool, &seed, sizeof(seed), _srealarray_init, _srealarray_init_copy, _srealarray_destroy, _state);
|
|
|
|
/*
|
|
* Call compute workhorse
|
|
*/
|
|
spline2d_computeresidualsfromscratchrec(xy, yraw, 0, npoints, chunksize, d, scalexy, spline, &pool, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_spline2d_computeresidualsfromscratch(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Recursive workhorse for ComputeResidualsFromScratch.
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_computeresidualsfromscratchrec(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
ae_int_t chunksize,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline,
|
|
ae_shared_pool* pool,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
srealarray *pbuf;
|
|
ae_smart_ptr _pbuf;
|
|
ae_int_t xew;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_pbuf, 0, sizeof(_pbuf));
|
|
ae_smart_ptr_init(&_pbuf, (void**)&pbuf, _state, ae_true);
|
|
|
|
xew = 2+d;
|
|
|
|
/*
|
|
* Parallelism
|
|
*/
|
|
if( pt1-pt0>chunksize )
|
|
{
|
|
tiledsplit(pt1-pt0, chunksize, &i, &j, _state);
|
|
spline2d_computeresidualsfromscratchrec(xy, yraw, pt0, pt0+i, chunksize, d, scalexy, spline, pool, _state);
|
|
spline2d_computeresidualsfromscratchrec(xy, yraw, pt0+i, pt1, chunksize, d, scalexy, spline, pool, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Serial execution
|
|
*/
|
|
ae_shared_pool_retrieve(pool, &_pbuf, _state);
|
|
for(i=pt0; i<=pt1-1; i++)
|
|
{
|
|
spline2dcalcvbuf(spline, xy->ptr.p_double[i*xew+0]*scalexy, xy->ptr.p_double[i*xew+1]*scalexy, &pbuf->val, _state);
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
xy->ptr.p_double[i*xew+2+j] = yraw->ptr.p_double[i*d+j]-pbuf->val.ptr.p_double[j];
|
|
}
|
|
}
|
|
ae_shared_pool_recycle(pool, &_pbuf, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_spline2d_computeresidualsfromscratchrec(/* Real */ ae_vector* xy,
|
|
/* Real */ ae_vector* yraw,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
ae_int_t chunksize,
|
|
ae_int_t d,
|
|
ae_int_t scalexy,
|
|
spline2dinterpolant* spline,
|
|
ae_shared_pool* pool,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function reorders dataset and builds index:
|
|
* it is assumed that all points have X in [0,KX-1], Y in [0,KY-1]
|
|
* area is divided into (KX-1)*(KY-1) cells
|
|
* all points are reordered in such way that points in same cell are stored
|
|
contiguously
|
|
* dataset index, array[(KX-1)*(KY-1)+1], is generated. Points of cell I
|
|
now have indexes XYIndex[I]..XYIndex[I+1]-1;
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints*(2+D)], dataset
|
|
KX, KY, D - grid size and dimensionality of the outputs
|
|
Shadow - shadow array[NPoints*NS], which is sorted together
|
|
with XY; if NS=0, it is not referenced at all.
|
|
NS - entry width of shadow array
|
|
BufI - possibly preallocated temporary buffer; resized if
|
|
needed.
|
|
|
|
OUTPUT PARAMETERS:
|
|
XY - reordered
|
|
XYIndex - array[(KX-1)*(KY-1)+1], dataset index
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_reorderdatasetandbuildindex(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Integer */ ae_vector* xyindex,
|
|
/* Integer */ ae_vector* bufi,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
ae_int_t entrywidth;
|
|
|
|
|
|
|
|
/*
|
|
* Set up
|
|
*/
|
|
ae_assert(kx>=2, "Spline2DFit.ReorderDatasetAndBuildIndex: integrity check failed", _state);
|
|
ae_assert(ky>=2, "Spline2DFit.ReorderDatasetAndBuildIndex: integrity check failed", _state);
|
|
entrywidth = 2+d;
|
|
ivectorsetlengthatleast(xyindex, (kx-1)*(ky-1)+1, _state);
|
|
ivectorsetlengthatleast(bufi, npoints, _state);
|
|
for(i=0; i<=npoints-1; i++)
|
|
{
|
|
i0 = iboundval(ae_ifloor(xy->ptr.p_double[i*entrywidth+0], _state), 0, kx-2, _state);
|
|
i1 = iboundval(ae_ifloor(xy->ptr.p_double[i*entrywidth+1], _state), 0, ky-2, _state);
|
|
bufi->ptr.p_int[i] = i1*(kx-1)+i0;
|
|
}
|
|
|
|
/*
|
|
* Reorder
|
|
*/
|
|
spline2d_reorderdatasetandbuildindexrec(xy, d, shadow, ns, bufi, 0, npoints, xyindex, 0, (kx-1)*(ky-1), ae_true, _state);
|
|
xyindex->ptr.p_int[(kx-1)*(ky-1)] = npoints;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function multiplies all points in dataset by 2.0 and rebuilds index,
|
|
given previous index built for KX_prev=(KX-1)/2 and KY_prev=(KY-1)/2
|
|
|
|
INPUT PARAMETERS:
|
|
XY - array[NPoints*(2+D)], dataset BEFORE scaling
|
|
NPoints, D - dataset size and dimensionality of the outputs
|
|
Shadow - shadow array[NPoints*NS], which is sorted together
|
|
with XY; if NS=0, it is not referenced at all.
|
|
NS - entry width of shadow array
|
|
KX, KY - new grid dimensionality
|
|
XYIndex - index built for previous values of KX and KY
|
|
BufI - possibly preallocated temporary buffer; resized if
|
|
needed.
|
|
|
|
OUTPUT PARAMETERS:
|
|
XY - reordered and multiplied by 2.0
|
|
XYIndex - array[(KX-1)*(KY-1)+1], dataset index
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_rescaledatasetandrefineindex(/* Real */ ae_vector* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
ae_int_t kx,
|
|
ae_int_t ky,
|
|
/* Integer */ ae_vector* xyindex,
|
|
/* Integer */ ae_vector* bufi,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_vector xyindexprev;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&xyindexprev, 0, sizeof(xyindexprev));
|
|
ae_vector_init(&xyindexprev, 0, DT_INT, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Set up
|
|
*/
|
|
ae_assert(kx>=2, "Spline2DFit.RescaleDataset2AndRefineIndex: integrity check failed", _state);
|
|
ae_assert(ky>=2, "Spline2DFit.RescaleDataset2AndRefineIndex: integrity check failed", _state);
|
|
ae_assert((kx-1)%2==0, "Spline2DFit.RescaleDataset2AndRefineIndex: integrity check failed", _state);
|
|
ae_assert((ky-1)%2==0, "Spline2DFit.RescaleDataset2AndRefineIndex: integrity check failed", _state);
|
|
ae_swap_vectors(xyindex, &xyindexprev);
|
|
ivectorsetlengthatleast(xyindex, (kx-1)*(ky-1)+1, _state);
|
|
ivectorsetlengthatleast(bufi, npoints, _state);
|
|
|
|
/*
|
|
* Refine
|
|
*/
|
|
spline2d_expandindexrows(xy, d, shadow, ns, bufi, 0, npoints, &xyindexprev, 0, (ky+1)/2-1, xyindex, kx, ky, ae_true, _state);
|
|
xyindex->ptr.p_int[(kx-1)*(ky-1)] = npoints;
|
|
|
|
/*
|
|
* Integrity check
|
|
*/
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Recurrent divide-and-conquer indexing function
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_expandindexrows(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindexprev,
|
|
ae_int_t row0,
|
|
ae_int_t row1,
|
|
/* Integer */ ae_vector* xyindexnew,
|
|
ae_int_t kxnew,
|
|
ae_int_t kynew,
|
|
ae_bool rootcall,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t entrywidth;
|
|
ae_int_t kxprev;
|
|
double v;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
double efficiency;
|
|
double cost;
|
|
ae_int_t rowmid;
|
|
|
|
|
|
kxprev = (kxnew+1)/2;
|
|
entrywidth = 2+d;
|
|
efficiency = 0.1;
|
|
cost = d*(pt1-pt0+1)*(ae_log((double)(kxnew), _state)/ae_log((double)(2), _state))/efficiency;
|
|
ae_assert(xyindexprev->ptr.p_int[row0*(kxprev-1)+0]==pt0, "Spline2DFit.ExpandIndexRows: integrity check failed", _state);
|
|
ae_assert(xyindexprev->ptr.p_int[row1*(kxprev-1)+0]==pt1, "Spline2DFit.ExpandIndexRows: integrity check failed", _state);
|
|
|
|
/*
|
|
* Parallelism
|
|
*/
|
|
if( ((rootcall&&pt1-pt0>10000)&&row1-row0>=2)&&ae_fp_greater(cost,smpactivationlevel(_state)) )
|
|
{
|
|
if( _trypexec_spline2d_expandindexrows(xy,d,shadow,ns,cidx,pt0,pt1,xyindexprev,row0,row1,xyindexnew,kxnew,kynew,rootcall, _state) )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Partition
|
|
*/
|
|
if( row1-row0>=2 )
|
|
{
|
|
tiledsplit(row1-row0, 1, &i0, &i1, _state);
|
|
rowmid = row0+i0;
|
|
spline2d_expandindexrows(xy, d, shadow, ns, cidx, pt0, xyindexprev->ptr.p_int[rowmid*(kxprev-1)+0], xyindexprev, row0, rowmid, xyindexnew, kxnew, kynew, ae_false, _state);
|
|
spline2d_expandindexrows(xy, d, shadow, ns, cidx, xyindexprev->ptr.p_int[rowmid*(kxprev-1)+0], pt1, xyindexprev, rowmid, row1, xyindexnew, kxnew, kynew, ae_false, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Serial execution
|
|
*/
|
|
for(i=pt0; i<=pt1-1; i++)
|
|
{
|
|
v = 2*xy->ptr.p_double[i*entrywidth+0];
|
|
xy->ptr.p_double[i*entrywidth+0] = v;
|
|
i0 = iboundval(ae_ifloor(v, _state), 0, kxnew-2, _state);
|
|
v = 2*xy->ptr.p_double[i*entrywidth+1];
|
|
xy->ptr.p_double[i*entrywidth+1] = v;
|
|
i1 = iboundval(ae_ifloor(v, _state), 0, kynew-2, _state);
|
|
cidx->ptr.p_int[i] = i1*(kxnew-1)+i0;
|
|
}
|
|
spline2d_reorderdatasetandbuildindexrec(xy, d, shadow, ns, cidx, pt0, pt1, xyindexnew, 2*row0*(kxnew-1)+0, 2*row1*(kxnew-1)+0, ae_false, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_spline2d_expandindexrows(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindexprev,
|
|
ae_int_t row0,
|
|
ae_int_t row1,
|
|
/* Integer */ ae_vector* xyindexnew,
|
|
ae_int_t kxnew,
|
|
ae_int_t kynew,
|
|
ae_bool rootcall,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Recurrent divide-and-conquer indexing function
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_reorderdatasetandbuildindexrec(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t idx0,
|
|
ae_int_t idx1,
|
|
ae_bool rootcall,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t entrywidth;
|
|
ae_int_t idxmid;
|
|
ae_int_t wrk0;
|
|
ae_int_t wrk1;
|
|
double efficiency;
|
|
double cost;
|
|
|
|
|
|
|
|
/*
|
|
* Efficiency - performance of the code when compared with that
|
|
* of linear algebra code.
|
|
*/
|
|
entrywidth = 2+d;
|
|
efficiency = 0.1;
|
|
cost = d*(pt1-pt0+1)*ae_log((double)(idx1-idx0+1), _state)/ae_log((double)(2), _state)/efficiency;
|
|
|
|
/*
|
|
* Parallelism
|
|
*/
|
|
if( ((rootcall&&pt1-pt0>10000)&&idx1-idx0>=2)&&ae_fp_greater(cost,smpactivationlevel(_state)) )
|
|
{
|
|
if( _trypexec_spline2d_reorderdatasetandbuildindexrec(xy,d,shadow,ns,cidx,pt0,pt1,xyindex,idx0,idx1,rootcall, _state) )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Store left bound to XYIndex
|
|
*/
|
|
xyindex->ptr.p_int[idx0] = pt0;
|
|
|
|
/*
|
|
* Quick exit strategies
|
|
*/
|
|
if( idx1<=idx0+1 )
|
|
{
|
|
return;
|
|
}
|
|
if( pt0==pt1 )
|
|
{
|
|
for(idxmid=idx0+1; idxmid<=idx1-1; idxmid++)
|
|
{
|
|
xyindex->ptr.p_int[idxmid] = pt1;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Select middle element
|
|
*/
|
|
idxmid = idx0+(idx1-idx0)/2;
|
|
ae_assert(idx0<idxmid&&idxmid<idx1, "Spline2D: integrity check failed", _state);
|
|
wrk0 = pt0;
|
|
wrk1 = pt1-1;
|
|
for(;;)
|
|
{
|
|
while(wrk0<pt1&&cidx->ptr.p_int[wrk0]<idxmid)
|
|
{
|
|
wrk0 = wrk0+1;
|
|
}
|
|
while(wrk1>=pt0&&cidx->ptr.p_int[wrk1]>=idxmid)
|
|
{
|
|
wrk1 = wrk1-1;
|
|
}
|
|
if( wrk1<=wrk0 )
|
|
{
|
|
break;
|
|
}
|
|
swapentries(xy, wrk0, wrk1, entrywidth, _state);
|
|
if( ns>0 )
|
|
{
|
|
swapentries(shadow, wrk0, wrk1, ns, _state);
|
|
}
|
|
swapelementsi(cidx, wrk0, wrk1, _state);
|
|
}
|
|
spline2d_reorderdatasetandbuildindexrec(xy, d, shadow, ns, cidx, pt0, wrk0, xyindex, idx0, idxmid, ae_false, _state);
|
|
spline2d_reorderdatasetandbuildindexrec(xy, d, shadow, ns, cidx, wrk0, pt1, xyindex, idxmid, idx1, ae_false, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_spline2d_reorderdatasetandbuildindexrec(/* Real */ ae_vector* xy,
|
|
ae_int_t d,
|
|
/* Real */ ae_vector* shadow,
|
|
ae_int_t ns,
|
|
/* Integer */ ae_vector* cidx,
|
|
ae_int_t pt0,
|
|
ae_int_t pt1,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t idx0,
|
|
ae_int_t idx1,
|
|
ae_bool rootcall,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs fitting with BlockLLS solver. Internal function,
|
|
never use it directly.
|
|
|
|
INPUT PARAMETERS:
|
|
XY - dataset, array[NPoints,2+D]
|
|
XYIndex - dataset index, see ReorderDatasetAndBuildIndex() for more info
|
|
KX0, KX1- X-indices of basis functions to select and fit;
|
|
range [KX0,KX1) is processed
|
|
KXTotal - total number of indexes in the entire grid
|
|
KY0, KY1- Y-indices of basis functions to select and fit;
|
|
range [KY0,KY1) is processed
|
|
KYTotal - total number of indexes in the entire grid
|
|
D - number of components in vector-valued spline, D>=1
|
|
LambdaReg- regularization coefficient
|
|
LambdaNS- nonlinearity penalty, exactly zero value is specially handled
|
|
(entire set of rows is not added to the matrix)
|
|
Basis1 - single-dimensional B-spline
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
A - design matrix
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_xdesigngenerate(/* Real */ ae_vector* xy,
|
|
/* Integer */ ae_vector* xyindex,
|
|
ae_int_t kx0,
|
|
ae_int_t kx1,
|
|
ae_int_t kxtotal,
|
|
ae_int_t ky0,
|
|
ae_int_t ky1,
|
|
ae_int_t kytotal,
|
|
ae_int_t d,
|
|
double lambdareg,
|
|
double lambdans,
|
|
spline1dinterpolant* basis1,
|
|
spline2dxdesignmatrix* a,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t entrywidth;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
ae_int_t kx;
|
|
ae_int_t ky;
|
|
ae_int_t rowsdone;
|
|
ae_int_t batchesdone;
|
|
ae_int_t pt0;
|
|
ae_int_t pt1;
|
|
ae_int_t base0;
|
|
ae_int_t base1;
|
|
ae_int_t baseidx;
|
|
ae_int_t nzshift;
|
|
ae_int_t nzwidth;
|
|
ae_matrix d2x;
|
|
ae_matrix d2y;
|
|
ae_matrix dxy;
|
|
double v;
|
|
double v0;
|
|
double v1;
|
|
double v2;
|
|
double w0;
|
|
double w1;
|
|
double w2;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&d2x, 0, sizeof(d2x));
|
|
memset(&d2y, 0, sizeof(d2y));
|
|
memset(&dxy, 0, sizeof(dxy));
|
|
ae_matrix_init(&d2x, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&d2y, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_matrix_init(&dxy, 0, 0, DT_REAL, _state, ae_true);
|
|
|
|
nzshift = 1;
|
|
nzwidth = 4;
|
|
entrywidth = 2+d;
|
|
kx = kx1-kx0;
|
|
ky = ky1-ky0;
|
|
a->lambdareg = lambdareg;
|
|
a->blockwidth = 4;
|
|
a->kx = kx;
|
|
a->ky = ky;
|
|
a->d = d;
|
|
a->npoints = 0;
|
|
a->ndenserows = 0;
|
|
a->ndensebatches = 0;
|
|
a->maxbatch = 0;
|
|
for(j1=ky0; j1<=ky1-2; j1++)
|
|
{
|
|
for(j0=kx0; j0<=kx1-2; j0++)
|
|
{
|
|
i = xyindex->ptr.p_int[j1*(kxtotal-1)+j0+1]-xyindex->ptr.p_int[j1*(kxtotal-1)+j0];
|
|
a->npoints = a->npoints+i;
|
|
a->ndenserows = a->ndenserows+i;
|
|
a->ndensebatches = a->ndensebatches+1;
|
|
a->maxbatch = ae_maxint(a->maxbatch, i, _state);
|
|
}
|
|
}
|
|
if( ae_fp_neq(lambdans,(double)(0)) )
|
|
{
|
|
ae_assert(ae_fp_greater_eq(lambdans,(double)(0)), "Spline2DFit: integrity check failed", _state);
|
|
a->ndenserows = a->ndenserows+3*(kx-2)*(ky-2);
|
|
a->ndensebatches = a->ndensebatches+(kx-2)*(ky-2);
|
|
a->maxbatch = ae_maxint(a->maxbatch, 3, _state);
|
|
}
|
|
a->nrows = a->ndenserows+kx*ky;
|
|
rmatrixsetlengthatleast(&a->vals, a->ndenserows, a->blockwidth*a->blockwidth+d, _state);
|
|
ivectorsetlengthatleast(&a->batches, a->ndensebatches+1, _state);
|
|
ivectorsetlengthatleast(&a->batchbases, a->ndensebatches, _state);
|
|
|
|
/*
|
|
* Setup output counters
|
|
*/
|
|
batchesdone = 0;
|
|
rowsdone = 0;
|
|
|
|
/*
|
|
* Generate rows corresponding to dataset points
|
|
*/
|
|
ae_assert(kx>=nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(ky>=nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
rvectorsetlengthatleast(&a->tmp0, nzwidth, _state);
|
|
rvectorsetlengthatleast(&a->tmp1, nzwidth, _state);
|
|
a->batches.ptr.p_int[batchesdone] = 0;
|
|
for(j1=ky0; j1<=ky1-2; j1++)
|
|
{
|
|
for(j0=kx0; j0<=kx1-2; j0++)
|
|
{
|
|
pt0 = xyindex->ptr.p_int[j1*(kxtotal-1)+j0];
|
|
pt1 = xyindex->ptr.p_int[j1*(kxtotal-1)+j0+1];
|
|
base0 = iboundval(j0-kx0-nzshift, 0, kx-nzwidth, _state);
|
|
base1 = iboundval(j1-ky0-nzshift, 0, ky-nzwidth, _state);
|
|
baseidx = base1*kx+base0;
|
|
a->batchbases.ptr.p_int[batchesdone] = baseidx;
|
|
for(i=pt0; i<=pt1-1; i++)
|
|
{
|
|
for(k0=0; k0<=nzwidth-1; k0++)
|
|
{
|
|
a->tmp0.ptr.p_double[k0] = spline1dcalc(basis1, xy->ptr.p_double[i*entrywidth+0]-(base0+kx0+k0), _state);
|
|
}
|
|
for(k1=0; k1<=nzwidth-1; k1++)
|
|
{
|
|
a->tmp1.ptr.p_double[k1] = spline1dcalc(basis1, xy->ptr.p_double[i*entrywidth+1]-(base1+ky0+k1), _state);
|
|
}
|
|
for(k1=0; k1<=nzwidth-1; k1++)
|
|
{
|
|
for(k0=0; k0<=nzwidth-1; k0++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][k1*nzwidth+k0] = a->tmp0.ptr.p_double[k0]*a->tmp1.ptr.p_double[k1];
|
|
}
|
|
}
|
|
for(j=0; j<=d-1; j++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][nzwidth*nzwidth+j] = xy->ptr.p_double[i*entrywidth+2+j];
|
|
}
|
|
rowsdone = rowsdone+1;
|
|
}
|
|
batchesdone = batchesdone+1;
|
|
a->batches.ptr.p_int[batchesdone] = rowsdone;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate rows corresponding to nonlinearity penalty
|
|
*/
|
|
if( ae_fp_greater(lambdans,(double)(0)) )
|
|
{
|
|
|
|
/*
|
|
* Smoothing is applied. Because all grid nodes are same,
|
|
* we apply same smoothing kernel, which is calculated only
|
|
* once at the beginning of design matrix generation.
|
|
*/
|
|
ae_matrix_set_length(&d2x, 3, 3, _state);
|
|
ae_matrix_set_length(&d2y, 3, 3, _state);
|
|
ae_matrix_set_length(&dxy, 3, 3, _state);
|
|
for(j1=0; j1<=2; j1++)
|
|
{
|
|
for(j0=0; j0<=2; j0++)
|
|
{
|
|
d2x.ptr.pp_double[j0][j1] = 0.0;
|
|
d2y.ptr.pp_double[j0][j1] = 0.0;
|
|
dxy.ptr.pp_double[j0][j1] = 0.0;
|
|
}
|
|
}
|
|
for(k1=0; k1<=2; k1++)
|
|
{
|
|
for(k0=0; k0<=2; k0++)
|
|
{
|
|
spline1ddiff(basis1, (double)(-(k0-1)), &v0, &v1, &v2, _state);
|
|
spline1ddiff(basis1, (double)(-(k1-1)), &w0, &w1, &w2, _state);
|
|
d2x.ptr.pp_double[k0][k1] = d2x.ptr.pp_double[k0][k1]+v2*w0;
|
|
d2y.ptr.pp_double[k0][k1] = d2y.ptr.pp_double[k0][k1]+w2*v0;
|
|
dxy.ptr.pp_double[k0][k1] = dxy.ptr.pp_double[k0][k1]+v1*w1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Now, kernel is ready - apply it to all inner nodes of the grid.
|
|
*/
|
|
for(j1=1; j1<=ky-2; j1++)
|
|
{
|
|
for(j0=1; j0<=kx-2; j0++)
|
|
{
|
|
base0 = imax2(j0-2, 0, _state);
|
|
base1 = imax2(j1-2, 0, _state);
|
|
baseidx = base1*kx+base0;
|
|
a->batchbases.ptr.p_int[batchesdone] = baseidx;
|
|
|
|
/*
|
|
* d2F/dx2 term
|
|
*/
|
|
v = lambdans;
|
|
for(j=0; j<=nzwidth*nzwidth+d-1; j++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][j] = (double)(0);
|
|
}
|
|
for(k1=j1-1; k1<=j1+1; k1++)
|
|
{
|
|
for(k0=j0-1; k0<=j0+1; k0++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][nzwidth*(k1-base1)+(k0-base0)] = v*d2x.ptr.pp_double[1+(k0-j0)][1+(k1-j1)];
|
|
}
|
|
}
|
|
rowsdone = rowsdone+1;
|
|
|
|
/*
|
|
* d2F/dy2 term
|
|
*/
|
|
v = lambdans;
|
|
for(j=0; j<=nzwidth*nzwidth+d-1; j++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][j] = (double)(0);
|
|
}
|
|
for(k1=j1-1; k1<=j1+1; k1++)
|
|
{
|
|
for(k0=j0-1; k0<=j0+1; k0++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][nzwidth*(k1-base1)+(k0-base0)] = v*d2y.ptr.pp_double[1+(k0-j0)][1+(k1-j1)];
|
|
}
|
|
}
|
|
rowsdone = rowsdone+1;
|
|
|
|
/*
|
|
* 2*d2F/dxdy term
|
|
*/
|
|
v = ae_sqrt((double)(2), _state)*lambdans;
|
|
for(j=0; j<=nzwidth*nzwidth+d-1; j++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][j] = (double)(0);
|
|
}
|
|
for(k1=j1-1; k1<=j1+1; k1++)
|
|
{
|
|
for(k0=j0-1; k0<=j0+1; k0++)
|
|
{
|
|
a->vals.ptr.pp_double[rowsdone][nzwidth*(k1-base1)+(k0-base0)] = v*dxy.ptr.pp_double[1+(k0-j0)][1+(k1-j1)];
|
|
}
|
|
}
|
|
rowsdone = rowsdone+1;
|
|
batchesdone = batchesdone+1;
|
|
a->batches.ptr.p_int[batchesdone] = rowsdone;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Integrity post-check
|
|
*/
|
|
ae_assert(batchesdone==a->ndensebatches, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(rowsdone==a->ndenserows, "Spline2DFit: integrity check failed", _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs matrix-vector product of design matrix and dense
|
|
vector.
|
|
|
|
INPUT PARAMETERS:
|
|
A - design matrix, (a.nrows) X (a.kx*a.ky);
|
|
some fields of A are used for temporaries,
|
|
so it is non-constant.
|
|
X - array[A.KX*A.KY]
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - product, array[A.NRows], automatically allocated
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_xdesignmv(spline2dxdesignmatrix* a,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t bidx;
|
|
ae_int_t i;
|
|
ae_int_t cnt;
|
|
double v;
|
|
ae_int_t baseidx;
|
|
ae_int_t outidx;
|
|
ae_int_t batchsize;
|
|
ae_int_t kx;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
ae_int_t nzwidth;
|
|
|
|
|
|
nzwidth = 4;
|
|
ae_assert(a->blockwidth==nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(x->cnt>=a->kx*a->ky, "Spline2DFit: integrity check failed", _state);
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
rvectorsetlengthatleast(y, a->nrows, _state);
|
|
rvectorsetlengthatleast(&a->tmp0, nzwidth*nzwidth, _state);
|
|
rvectorsetlengthatleast(&a->tmp1, a->maxbatch, _state);
|
|
kx = a->kx;
|
|
outidx = 0;
|
|
|
|
/*
|
|
* Process dense part
|
|
*/
|
|
for(bidx=0; bidx<=a->ndensebatches-1; bidx++)
|
|
{
|
|
if( a->batches.ptr.p_int[bidx+1]-a->batches.ptr.p_int[bidx]>0 )
|
|
{
|
|
batchsize = a->batches.ptr.p_int[bidx+1]-a->batches.ptr.p_int[bidx];
|
|
baseidx = a->batchbases.ptr.p_int[bidx];
|
|
for(k1=0; k1<=nzwidth-1; k1++)
|
|
{
|
|
for(k0=0; k0<=nzwidth-1; k0++)
|
|
{
|
|
a->tmp0.ptr.p_double[k1*nzwidth+k0] = x->ptr.p_double[baseidx+k1*kx+k0];
|
|
}
|
|
}
|
|
rmatrixgemv(batchsize, nzwidth*nzwidth, 1.0, &a->vals, a->batches.ptr.p_int[bidx], 0, 0, &a->tmp0, 0, 0.0, &a->tmp1, 0, _state);
|
|
for(i=0; i<=batchsize-1; i++)
|
|
{
|
|
y->ptr.p_double[outidx+i] = a->tmp1.ptr.p_double[i];
|
|
}
|
|
outidx = outidx+batchsize;
|
|
}
|
|
}
|
|
ae_assert(outidx==a->ndenserows, "Spline2DFit: integrity check failed", _state);
|
|
|
|
/*
|
|
* Process regularizer
|
|
*/
|
|
v = a->lambdareg;
|
|
cnt = a->kx*a->ky;
|
|
for(i=0; i<=cnt-1; i++)
|
|
{
|
|
y->ptr.p_double[outidx+i] = v*x->ptr.p_double[i];
|
|
}
|
|
outidx = outidx+cnt;
|
|
|
|
/*
|
|
* Post-check
|
|
*/
|
|
ae_assert(outidx==a->nrows, "Spline2DFit: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function performs matrix-vector product of transposed design matrix and dense
|
|
vector.
|
|
|
|
INPUT PARAMETERS:
|
|
A - design matrix, (a.nrows) X (a.kx*a.ky);
|
|
some fields of A are used for temporaries,
|
|
so it is non-constant.
|
|
X - array[A.NRows]
|
|
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - product, array[A.KX*A.KY], automatically allocated
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_xdesignmtv(spline2dxdesignmatrix* a,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t bidx;
|
|
ae_int_t i;
|
|
ae_int_t cnt;
|
|
double v;
|
|
ae_int_t baseidx;
|
|
ae_int_t inidx;
|
|
ae_int_t batchsize;
|
|
ae_int_t kx;
|
|
ae_int_t k0;
|
|
ae_int_t k1;
|
|
ae_int_t nzwidth;
|
|
|
|
|
|
nzwidth = 4;
|
|
ae_assert(a->blockwidth==nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
ae_assert(x->cnt>=a->nrows, "Spline2DFit: integrity check failed", _state);
|
|
|
|
/*
|
|
* Prepare
|
|
*/
|
|
rvectorsetlengthatleast(y, a->kx*a->ky, _state);
|
|
rvectorsetlengthatleast(&a->tmp0, nzwidth*nzwidth, _state);
|
|
rvectorsetlengthatleast(&a->tmp1, a->maxbatch, _state);
|
|
kx = a->kx;
|
|
inidx = 0;
|
|
cnt = a->kx*a->ky;
|
|
for(i=0; i<=cnt-1; i++)
|
|
{
|
|
y->ptr.p_double[i] = (double)(0);
|
|
}
|
|
|
|
/*
|
|
* Process dense part
|
|
*/
|
|
for(bidx=0; bidx<=a->ndensebatches-1; bidx++)
|
|
{
|
|
if( a->batches.ptr.p_int[bidx+1]-a->batches.ptr.p_int[bidx]>0 )
|
|
{
|
|
batchsize = a->batches.ptr.p_int[bidx+1]-a->batches.ptr.p_int[bidx];
|
|
baseidx = a->batchbases.ptr.p_int[bidx];
|
|
for(i=0; i<=batchsize-1; i++)
|
|
{
|
|
a->tmp1.ptr.p_double[i] = x->ptr.p_double[inidx+i];
|
|
}
|
|
rmatrixgemv(nzwidth*nzwidth, batchsize, 1.0, &a->vals, a->batches.ptr.p_int[bidx], 0, 1, &a->tmp1, 0, 0.0, &a->tmp0, 0, _state);
|
|
for(k1=0; k1<=nzwidth-1; k1++)
|
|
{
|
|
for(k0=0; k0<=nzwidth-1; k0++)
|
|
{
|
|
y->ptr.p_double[baseidx+k1*kx+k0] = y->ptr.p_double[baseidx+k1*kx+k0]+a->tmp0.ptr.p_double[k1*nzwidth+k0];
|
|
}
|
|
}
|
|
inidx = inidx+batchsize;
|
|
}
|
|
}
|
|
ae_assert(inidx==a->ndenserows, "Spline2DFit: integrity check failed", _state);
|
|
|
|
/*
|
|
* Process regularizer
|
|
*/
|
|
v = a->lambdareg;
|
|
cnt = a->kx*a->ky;
|
|
for(i=0; i<=cnt-1; i++)
|
|
{
|
|
y->ptr.p_double[i] = y->ptr.p_double[i]+v*x->ptr.p_double[inidx+i];
|
|
}
|
|
inidx = inidx+cnt;
|
|
|
|
/*
|
|
* Post-check
|
|
*/
|
|
ae_assert(inidx==a->nrows, "Spline2DFit: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function generates squared design matrix stored in block band format.
|
|
|
|
We use an adaptation of block skyline storage format, with
|
|
TOWERSIZE*KX skyline bands (towers) stored sequentially; here
|
|
TOWERSIZE=(BlockBandwidth+1)*KX. So, we have KY "towers", stored one below
|
|
other, in BlockATA matrix. Every "tower" is a sequence of BlockBandwidth+1
|
|
cells, each of them being KX*KX in size.
|
|
|
|
INPUT PARAMETERS:
|
|
A - design matrix; some of its fields are used for temporaries
|
|
BlockATA- array[KY*(BlockBandwidth+1)*KX,KX], preallocated storage
|
|
for output matrix in compressed block band format
|
|
|
|
OUTPUT PARAMETERS:
|
|
BlockATA- AH*AH', stored in compressed block band format
|
|
MXATA - max(|AH*AH'|), elementwise
|
|
|
|
-- ALGLIB --
|
|
Copyright 05.02.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void spline2d_xdesignblockata(spline2dxdesignmatrix* a,
|
|
/* Real */ ae_matrix* blockata,
|
|
double* mxata,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t blockbandwidth;
|
|
ae_int_t nzwidth;
|
|
ae_int_t kx;
|
|
ae_int_t ky;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
ae_int_t j0;
|
|
ae_int_t j1;
|
|
ae_int_t celloffset;
|
|
ae_int_t bidx;
|
|
ae_int_t baseidx;
|
|
ae_int_t batchsize;
|
|
ae_int_t offs0;
|
|
ae_int_t offs1;
|
|
double v;
|
|
|
|
|
|
blockbandwidth = 3;
|
|
nzwidth = 4;
|
|
kx = a->kx;
|
|
ky = a->ky;
|
|
ae_assert(a->blockwidth==nzwidth, "Spline2DFit: integrity check failed", _state);
|
|
rmatrixsetlengthatleast(&a->tmp2, nzwidth*nzwidth, nzwidth*nzwidth, _state);
|
|
|
|
/*
|
|
* Initial zero-fill:
|
|
* * zero-fill ALL elements of BlockATA
|
|
* * zero-fill ALL elements of Tmp2
|
|
*
|
|
* Filling ALL elements, including unused ones, is essential for the
|
|
* purposes of calculating max(BlockATA).
|
|
*/
|
|
for(i1=0; i1<=ky-1; i1++)
|
|
{
|
|
for(i0=i1; i0<=ae_minint(ky-1, i1+blockbandwidth, _state); i0++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, i1, i0, _state);
|
|
for(j1=0; j1<=kx-1; j1++)
|
|
{
|
|
for(j0=0; j0<=kx-1; j0++)
|
|
{
|
|
blockata->ptr.pp_double[celloffset+j1][j0] = 0.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for(j1=0; j1<=nzwidth*nzwidth-1; j1++)
|
|
{
|
|
for(j0=0; j0<=nzwidth*nzwidth-1; j0++)
|
|
{
|
|
a->tmp2.ptr.pp_double[j1][j0] = 0.0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process dense part of A
|
|
*/
|
|
for(bidx=0; bidx<=a->ndensebatches-1; bidx++)
|
|
{
|
|
if( a->batches.ptr.p_int[bidx+1]-a->batches.ptr.p_int[bidx]>0 )
|
|
{
|
|
|
|
/*
|
|
* Generate 16x16 U = BATCH'*BATCH and add it to ATA.
|
|
*
|
|
* NOTE: it is essential that lower triangle of Tmp2 is
|
|
* filled by zeros.
|
|
*/
|
|
batchsize = a->batches.ptr.p_int[bidx+1]-a->batches.ptr.p_int[bidx];
|
|
rmatrixsyrk(nzwidth*nzwidth, batchsize, 1.0, &a->vals, a->batches.ptr.p_int[bidx], 0, 2, 0.0, &a->tmp2, 0, 0, ae_true, _state);
|
|
baseidx = a->batchbases.ptr.p_int[bidx];
|
|
for(i1=0; i1<=nzwidth-1; i1++)
|
|
{
|
|
for(j1=i1; j1<=nzwidth-1; j1++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, baseidx/kx+i1, baseidx/kx+j1, _state);
|
|
offs0 = baseidx%kx;
|
|
offs1 = baseidx%kx;
|
|
for(i0=0; i0<=nzwidth-1; i0++)
|
|
{
|
|
for(j0=0; j0<=nzwidth-1; j0++)
|
|
{
|
|
v = a->tmp2.ptr.pp_double[i1*nzwidth+i0][j1*nzwidth+j0];
|
|
blockata->ptr.pp_double[celloffset+offs1+i0][offs0+j0] = blockata->ptr.pp_double[celloffset+offs1+i0][offs0+j0]+v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Process regularizer term
|
|
*/
|
|
for(i1=0; i1<=ky-1; i1++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, i1, i1, _state);
|
|
for(j1=0; j1<=kx-1; j1++)
|
|
{
|
|
blockata->ptr.pp_double[celloffset+j1][j1] = blockata->ptr.pp_double[celloffset+j1][j1]+ae_sqr(a->lambdareg, _state);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Calculate max(ATA)
|
|
*
|
|
* NOTE: here we rely on zero initialization of unused parts of
|
|
* BlockATA and Tmp2.
|
|
*/
|
|
*mxata = 0.0;
|
|
for(i1=0; i1<=ky-1; i1++)
|
|
{
|
|
for(i0=i1; i0<=ae_minint(ky-1, i1+blockbandwidth, _state); i0++)
|
|
{
|
|
celloffset = spline2d_getcelloffset(kx, ky, blockbandwidth, i1, i0, _state);
|
|
for(j1=0; j1<=kx-1; j1++)
|
|
{
|
|
for(j0=0; j0<=kx-1; j0++)
|
|
{
|
|
*mxata = ae_maxreal(*mxata, ae_fabs(blockata->ptr.pp_double[celloffset+j1][j0], _state), _state);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void _spline2dinterpolant_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dinterpolant *p = (spline2dinterpolant*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->x, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->y, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->f, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dinterpolant_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dinterpolant *dst = (spline2dinterpolant*)_dst;
|
|
spline2dinterpolant *src = (spline2dinterpolant*)_src;
|
|
dst->stype = src->stype;
|
|
dst->n = src->n;
|
|
dst->m = src->m;
|
|
dst->d = src->d;
|
|
ae_vector_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->y, &src->y, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->f, &src->f, _state, make_automatic);
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
void _spline2dbuilder_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dbuilder *p = (spline2dbuilder*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->xy, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dbuilder_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dbuilder *dst = (spline2dbuilder*)_dst;
|
|
spline2dbuilder *src = (spline2dbuilder*)_src;
|
|
dst->priorterm = src->priorterm;
|
|
dst->priortermval = src->priortermval;
|
|
dst->areatype = src->areatype;
|
|
dst->xa = src->xa;
|
|
dst->xb = src->xb;
|
|
dst->ya = src->ya;
|
|
dst->yb = src->yb;
|
|
dst->gridtype = src->gridtype;
|
|
dst->kx = src->kx;
|
|
dst->ky = src->ky;
|
|
dst->smoothing = src->smoothing;
|
|
dst->nlayers = src->nlayers;
|
|
dst->solvertype = src->solvertype;
|
|
dst->lambdabase = src->lambdabase;
|
|
ae_vector_init_copy(&dst->xy, &src->xy, _state, make_automatic);
|
|
dst->npoints = src->npoints;
|
|
dst->d = src->d;
|
|
dst->sx = src->sx;
|
|
dst->sy = src->sy;
|
|
dst->adddegreeoffreedom = src->adddegreeoffreedom;
|
|
dst->interfacesize = src->interfacesize;
|
|
dst->lsqrcnt = src->lsqrcnt;
|
|
dst->maxcoresize = src->maxcoresize;
|
|
}
|
|
|
|
|
|
void _spline2dbuilder_clear(void* _p)
|
|
{
|
|
spline2dbuilder *p = (spline2dbuilder*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->xy);
|
|
}
|
|
|
|
|
|
void _spline2dbuilder_destroy(void* _p)
|
|
{
|
|
spline2dbuilder *p = (spline2dbuilder*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->xy);
|
|
}
|
|
|
|
|
|
void _spline2dfitreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dfitreport *p = (spline2dfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _spline2dfitreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dfitreport *dst = (spline2dfitreport*)_dst;
|
|
spline2dfitreport *src = (spline2dfitreport*)_src;
|
|
dst->rmserror = src->rmserror;
|
|
dst->avgerror = src->avgerror;
|
|
dst->maxerror = src->maxerror;
|
|
dst->r2 = src->r2;
|
|
}
|
|
|
|
|
|
void _spline2dfitreport_clear(void* _p)
|
|
{
|
|
spline2dfitreport *p = (spline2dfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _spline2dfitreport_destroy(void* _p)
|
|
{
|
|
spline2dfitreport *p = (spline2dfitreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _spline2dxdesignmatrix_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dxdesignmatrix *p = (spline2dxdesignmatrix*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_matrix_init(&p->vals, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->batches, 0, DT_INT, _state, make_automatic);
|
|
ae_vector_init(&p->batchbases, 0, DT_INT, _state, make_automatic);
|
|
ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->tmp2, 0, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dxdesignmatrix_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dxdesignmatrix *dst = (spline2dxdesignmatrix*)_dst;
|
|
spline2dxdesignmatrix *src = (spline2dxdesignmatrix*)_src;
|
|
dst->blockwidth = src->blockwidth;
|
|
dst->kx = src->kx;
|
|
dst->ky = src->ky;
|
|
dst->npoints = src->npoints;
|
|
dst->nrows = src->nrows;
|
|
dst->ndenserows = src->ndenserows;
|
|
dst->ndensebatches = src->ndensebatches;
|
|
dst->d = src->d;
|
|
dst->maxbatch = src->maxbatch;
|
|
ae_matrix_init_copy(&dst->vals, &src->vals, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->batches, &src->batches, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->batchbases, &src->batchbases, _state, make_automatic);
|
|
dst->lambdareg = src->lambdareg;
|
|
ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->tmp2, &src->tmp2, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dxdesignmatrix_clear(void* _p)
|
|
{
|
|
spline2dxdesignmatrix *p = (spline2dxdesignmatrix*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_matrix_clear(&p->vals);
|
|
ae_vector_clear(&p->batches);
|
|
ae_vector_clear(&p->batchbases);
|
|
ae_vector_clear(&p->tmp0);
|
|
ae_vector_clear(&p->tmp1);
|
|
ae_matrix_clear(&p->tmp2);
|
|
}
|
|
|
|
|
|
void _spline2dxdesignmatrix_destroy(void* _p)
|
|
{
|
|
spline2dxdesignmatrix *p = (spline2dxdesignmatrix*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_matrix_destroy(&p->vals);
|
|
ae_vector_destroy(&p->batches);
|
|
ae_vector_destroy(&p->batchbases);
|
|
ae_vector_destroy(&p->tmp0);
|
|
ae_vector_destroy(&p->tmp1);
|
|
ae_matrix_destroy(&p->tmp2);
|
|
}
|
|
|
|
|
|
void _spline2dblockllsbuf_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dblockllsbuf *p = (spline2dblockllsbuf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_linlsqrstate_init(&p->solver, _state, make_automatic);
|
|
_linlsqrreport_init(&p->solverrep, _state, make_automatic);
|
|
ae_matrix_init(&p->blockata, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->trsmbuf2, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->cholbuf2, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->cholbuf1, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmp1, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dblockllsbuf_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dblockllsbuf *dst = (spline2dblockllsbuf*)_dst;
|
|
spline2dblockllsbuf *src = (spline2dblockllsbuf*)_src;
|
|
_linlsqrstate_init_copy(&dst->solver, &src->solver, _state, make_automatic);
|
|
_linlsqrreport_init_copy(&dst->solverrep, &src->solverrep, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->blockata, &src->blockata, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->trsmbuf2, &src->trsmbuf2, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->cholbuf2, &src->cholbuf2, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->cholbuf1, &src->cholbuf1, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmp1, &src->tmp1, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dblockllsbuf_clear(void* _p)
|
|
{
|
|
spline2dblockllsbuf *p = (spline2dblockllsbuf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_linlsqrstate_clear(&p->solver);
|
|
_linlsqrreport_clear(&p->solverrep);
|
|
ae_matrix_clear(&p->blockata);
|
|
ae_matrix_clear(&p->trsmbuf2);
|
|
ae_matrix_clear(&p->cholbuf2);
|
|
ae_vector_clear(&p->cholbuf1);
|
|
ae_vector_clear(&p->tmp0);
|
|
ae_vector_clear(&p->tmp1);
|
|
}
|
|
|
|
|
|
void _spline2dblockllsbuf_destroy(void* _p)
|
|
{
|
|
spline2dblockllsbuf *p = (spline2dblockllsbuf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_linlsqrstate_destroy(&p->solver);
|
|
_linlsqrreport_destroy(&p->solverrep);
|
|
ae_matrix_destroy(&p->blockata);
|
|
ae_matrix_destroy(&p->trsmbuf2);
|
|
ae_matrix_destroy(&p->cholbuf2);
|
|
ae_vector_destroy(&p->cholbuf1);
|
|
ae_vector_destroy(&p->tmp0);
|
|
ae_vector_destroy(&p->tmp1);
|
|
}
|
|
|
|
|
|
void _spline2dfastddmbuf_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dfastddmbuf *p = (spline2dfastddmbuf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_spline2dxdesignmatrix_init(&p->xdesignmatrix, _state, make_automatic);
|
|
ae_vector_init(&p->tmp0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->tmpz, 0, DT_REAL, _state, make_automatic);
|
|
_spline2dfitreport_init(&p->dummyrep, _state, make_automatic);
|
|
_spline2dinterpolant_init(&p->localmodel, _state, make_automatic);
|
|
_spline2dblockllsbuf_init(&p->blockllsbuf, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dfastddmbuf_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
spline2dfastddmbuf *dst = (spline2dfastddmbuf*)_dst;
|
|
spline2dfastddmbuf *src = (spline2dfastddmbuf*)_src;
|
|
_spline2dxdesignmatrix_init_copy(&dst->xdesignmatrix, &src->xdesignmatrix, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmp0, &src->tmp0, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->tmpz, &src->tmpz, _state, make_automatic);
|
|
_spline2dfitreport_init_copy(&dst->dummyrep, &src->dummyrep, _state, make_automatic);
|
|
_spline2dinterpolant_init_copy(&dst->localmodel, &src->localmodel, _state, make_automatic);
|
|
_spline2dblockllsbuf_init_copy(&dst->blockllsbuf, &src->blockllsbuf, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _spline2dfastddmbuf_clear(void* _p)
|
|
{
|
|
spline2dfastddmbuf *p = (spline2dfastddmbuf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_spline2dxdesignmatrix_clear(&p->xdesignmatrix);
|
|
ae_vector_clear(&p->tmp0);
|
|
ae_vector_clear(&p->tmpz);
|
|
_spline2dfitreport_clear(&p->dummyrep);
|
|
_spline2dinterpolant_clear(&p->localmodel);
|
|
_spline2dblockllsbuf_clear(&p->blockllsbuf);
|
|
}
|
|
|
|
|
|
void _spline2dfastddmbuf_destroy(void* _p)
|
|
{
|
|
spline2dfastddmbuf *p = (spline2dfastddmbuf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_spline2dxdesignmatrix_destroy(&p->xdesignmatrix);
|
|
ae_vector_destroy(&p->tmp0);
|
|
ae_vector_destroy(&p->tmpz);
|
|
_spline2dfitreport_destroy(&p->dummyrep);
|
|
_spline2dinterpolant_destroy(&p->localmodel);
|
|
_spline2dblockllsbuf_destroy(&p->blockllsbuf);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RBFV1) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
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).
|
|
|
|
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)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv1create(ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv1model* s,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
_rbfv1model_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, rbfv1_mxnx+1, _state);
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbfv1_mxnx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
s->rmax = (double)(0);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function creates buffer structure which can be used to perform
|
|
parallel RBF model evaluations (with one RBF model instance being
|
|
used from multiple threads, as long as different threads use different
|
|
instances of buffer).
|
|
|
|
This buffer object can be used with rbftscalcbuf() function (here "ts"
|
|
stands for "thread-safe", "buf" is a suffix which denotes function which
|
|
reuses previously allocated output space).
|
|
|
|
How to use it:
|
|
* create RBF model structure with rbfcreate()
|
|
* load data, tune parameters
|
|
* call rbfbuildmodel()
|
|
* call rbfcreatecalcbuffer(), once per thread working with RBF model (you
|
|
should call this function only AFTER call to rbfbuildmodel(), see below
|
|
for more information)
|
|
* call rbftscalcbuf() from different threads, with each thread working
|
|
with its own copy of buffer object.
|
|
|
|
INPUT PARAMETERS
|
|
S - RBF model
|
|
|
|
OUTPUT PARAMETERS
|
|
Buf - external buffer.
|
|
|
|
|
|
IMPORTANT: buffer object should be used only with RBF model object which
|
|
was used to initialize buffer. Any attempt to use buffer with
|
|
different object is dangerous - you may get memory violation
|
|
error because sizes of internal arrays do not fit to dimensions
|
|
of RBF structure.
|
|
|
|
IMPORTANT: you should call this function only for model which was built
|
|
with rbfbuildmodel() function, after successful invocation of
|
|
rbfbuildmodel(). Sizes of some internal structures are
|
|
determined only after model is built, so buffer object created
|
|
before model construction stage will be useless (and any
|
|
attempt to use it will result in exception).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.04.2016 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
void rbfv1createcalcbuffer(rbfv1model* s,
|
|
rbfv1calcbuffer* buf,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_rbfv1calcbuffer_clear(buf);
|
|
|
|
kdtreecreaterequestbuffer(&s->tree, &buf->requestbuffer, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 rbfv1buildmodel(/* Real */ ae_matrix* x,
|
|
/* Real */ ae_matrix* y,
|
|
ae_int_t n,
|
|
ae_int_t aterm,
|
|
ae_int_t algorithmtype,
|
|
ae_int_t nlayers,
|
|
double radvalue,
|
|
double radzvalue,
|
|
double lambdav,
|
|
double epsort,
|
|
double epserr,
|
|
ae_int_t maxits,
|
|
rbfv1model* s,
|
|
rbfv1report* 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_matrix residualy;
|
|
ae_vector radius;
|
|
ae_matrix xc;
|
|
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 snnz;
|
|
ae_vector tmp0;
|
|
ae_vector tmp1;
|
|
ae_int_t layerscnt;
|
|
ae_bool modelstatus;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&tree, 0, sizeof(tree));
|
|
memset(&ctree, 0, sizeof(ctree));
|
|
memset(&dist, 0, sizeof(dist));
|
|
memset(&xcx, 0, sizeof(xcx));
|
|
memset(&a, 0, sizeof(a));
|
|
memset(&v, 0, sizeof(v));
|
|
memset(&omega, 0, sizeof(omega));
|
|
memset(&residualy, 0, sizeof(residualy));
|
|
memset(&radius, 0, sizeof(radius));
|
|
memset(&xc, 0, sizeof(xc));
|
|
memset(&tags, 0, sizeof(tags));
|
|
memset(&ctags, 0, sizeof(ctags));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
memset(&tmp1, 0, sizeof(tmp1));
|
|
_rbfv1report_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_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(&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( n==0 )
|
|
{
|
|
rep->terminationtype = 1;
|
|
rep->iterationscount = 0;
|
|
rep->nmv = 0;
|
|
rep->arows = 0;
|
|
rep->acols = 0;
|
|
kdtreebuildtagged(&s->xc, &tags, 0, rbfv1_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 = (double)(0);
|
|
ae_matrix_set_length(&s->v, s->ny, rbfv1_mxnx+1, _state);
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbfv1_mxnx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* General case, N>0
|
|
*/
|
|
rep->annz = 0;
|
|
rep->iterationscount = 0;
|
|
rep->nmv = 0;
|
|
ae_vector_set_length(&xcx, rbfv1_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, n, s->ny, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=s->ny-1; j++)
|
|
{
|
|
residualy.ptr.pp_double[i][j] = y->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
if( !rbfv1_rbfv1buildlinearmodel(x, &residualy, n, s->ny, aterm, &v, _state) )
|
|
{
|
|
rep->terminationtype = -5;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Handle special case: multilayer model with NLayers=0.
|
|
* Quick exit.
|
|
*/
|
|
if( algorithmtype==2&&nlayers==0 )
|
|
{
|
|
rep->terminationtype = 1;
|
|
rep->iterationscount = 0;
|
|
rep->nmv = 0;
|
|
rep->arows = 0;
|
|
rep->acols = 0;
|
|
kdtreebuildtagged(&s->xc, &tags, 0, rbfv1_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 = (double)(0);
|
|
ae_matrix_set_length(&s->v, s->ny, rbfv1_mxnx+1, _state);
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
for(j=0; j<=rbfv1_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 = (double)(0);
|
|
layerscnt = 0;
|
|
modelstatus = ae_false;
|
|
if( algorithmtype==1 )
|
|
{
|
|
|
|
/*
|
|
* Add RBF model.
|
|
* This model uses local KD-trees to speed-up nearest neighbor searches.
|
|
*/
|
|
nc = n;
|
|
ae_matrix_set_length(&xc, nc, rbfv1_mxnx, _state);
|
|
for(i=0; i<=nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbfv1_mxnx-1; j++)
|
|
{
|
|
xc.ptr.pp_double[i][j] = x->ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
rmax = (double)(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, rbfv1_mxnx, 0, 2, &ctree, _state);
|
|
if( nc==0 )
|
|
{
|
|
rmax = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
if( nc==1 )
|
|
{
|
|
radius.ptr.p_double[0] = 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<=rbfv1_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] = 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], 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],(double)(0)) )
|
|
{
|
|
rep->terminationtype = -5;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ivectorsetlengthatleast(&tags, n, _state);
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
tags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(x, &tags, n, rbfv1_mxnx, 0, 2, &tree, _state);
|
|
rbfv1_buildrbfmodellsqr(x, &residualy, &xc, &radius, n, nc, s->ny, &tree, &ctree, epsort, epserr, maxits, &rep->annz, &snnz, &omega, &rep->terminationtype, &rep->iterationscount, &rep->nmv, _state);
|
|
layerscnt = 1;
|
|
modelstatus = ae_true;
|
|
}
|
|
if( algorithmtype==2 )
|
|
{
|
|
rmax = radvalue;
|
|
rbfv1_buildrbfmlayersmodellsqr(x, &residualy, &xc, radvalue, &radius, n, &nc, s->ny, nlayers, &ctree, 1.0E-6, 1.0E-6, 50, lambdav, &rep->annz, &omega, &rep->terminationtype, &rep->iterationscount, &rep->nmv, _state);
|
|
layerscnt = nlayers;
|
|
modelstatus = ae_true;
|
|
}
|
|
ae_assert(modelstatus, "RBFBuildModel: integrity error", _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, rbfv1_mxnx, _state);
|
|
ae_matrix_set_length(&s->wr, s->nc, 1+s->nl*s->ny, _state);
|
|
ae_matrix_set_length(&s->v, s->ny, rbfv1_mxnx+1, _state);
|
|
for(i=0; i<=s->nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbfv1_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, rbfv1_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<=rbfv1_mxnx; j++)
|
|
{
|
|
s->v.ptr.pp_double[i][j] = v.ptr.pp_double[i][j];
|
|
}
|
|
}
|
|
rep->terminationtype = 1;
|
|
rep->arows = n;
|
|
rep->acols = s->nc;
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serializer: allocation
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.02.2012 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv1alloc(ae_serializer* s, rbfv1model* model, ae_state *_state)
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
* 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 rbfv1serialize(ae_serializer* s, rbfv1model* model, ae_state *_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 rbfv1unserialize(ae_serializer* s,
|
|
rbfv1model* model,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
|
|
_rbfv1model_clear(model);
|
|
|
|
|
|
/*
|
|
* 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);
|
|
rbfv1create(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 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 rbfv1calc2(rbfv1model* 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 = (double)(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][rbfv1_mxnx];
|
|
if( s->nc==0 )
|
|
{
|
|
return result;
|
|
}
|
|
rvectorsetlengthatleast(&s->calcbufxcx, rbfv1_mxnx, _state);
|
|
for(i=0; i<=rbfv1_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*rbfv1_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 rbfv1calc3(rbfv1model* 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 = (double)(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][rbfv1_mxnx];
|
|
if( s->nc==0 )
|
|
{
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* calculating value for F(X)
|
|
*/
|
|
rvectorsetlengthatleast(&s->calcbufxcx, rbfv1_mxnx, _state);
|
|
for(i=0; i<=rbfv1_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*rbfv1_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.
|
|
|
|
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 rbfv1calcbuf(rbfv1model* 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][rbfv1_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, rbfv1_mxnx, _state);
|
|
for(i=0; i<=rbfv1_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*rbfv1_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 given point, using
|
|
external buffer object (internal temporaries of RBF model are not
|
|
modified).
|
|
|
|
This function allows to use same RBF model object in different threads,
|
|
assuming that different threads use different instances of buffer
|
|
structure.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, may be shared between different threads
|
|
Buf - buffer object created for this particular instance of RBF
|
|
model with rbfcreatecalcbuffer().
|
|
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 rbfv1tscalcbuf(rbfv1model* s,
|
|
rbfv1calcbuffer* buf,
|
|
/* 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][rbfv1_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(&buf->calcbufxcx, rbfv1_mxnx, _state);
|
|
for(i=0; i<=rbfv1_mxnx-1; i++)
|
|
{
|
|
buf->calcbufxcx.ptr.p_double[i] = 0.0;
|
|
}
|
|
for(i=0; i<=s->nx-1; i++)
|
|
{
|
|
buf->calcbufxcx.ptr.p_double[i] = x->ptr.p_double[i];
|
|
}
|
|
lx = kdtreetsqueryrnn(&s->tree, &buf->requestbuffer, &buf->calcbufxcx, s->rmax*rbfv1_rbffarradius, ae_true, _state);
|
|
kdtreetsqueryresultsx(&s->tree, &buf->requestbuffer, &buf->calcbufx, _state);
|
|
kdtreetsqueryresultstags(&s->tree, &buf->requestbuffer, &buf->calcbuftags, _state);
|
|
for(i=0; i<=s->ny-1; i++)
|
|
{
|
|
for(j=0; j<=lx-1; j++)
|
|
{
|
|
tg = buf->calcbuftags.ptr.p_int[j];
|
|
rcur = s->wr.ptr.pp_double[tg][0];
|
|
bf = ae_exp(-(ae_sqr(buf->calcbufxcx.ptr.p_double[0]-buf->calcbufx.ptr.pp_double[j][0], _state)+ae_sqr(buf->calcbufxcx.ptr.p_double[1]-buf->calcbufx.ptr.pp_double[j][1], _state)+ae_sqr(buf->calcbufxcx.ptr.p_double[2]-buf->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.
|
|
|
|
NOTE: as a special exception, this function supports unordered arrays X0
|
|
and X1. However, future versions may be more efficient for X0/X1
|
|
ordered by ascending.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfv1gridcalc2(rbfv1model* 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);
|
|
memset(&cpx0, 0, sizeof(cpx0));
|
|
memset(&cpx1, 0, sizeof(cpx1));
|
|
memset(&p01, 0, sizeof(p01));
|
|
memset(&p11, 0, sizeof(p11));
|
|
memset(&p2, 0, sizeof(p2));
|
|
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] = (double)(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*rbfv1_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][rbfv1_mxnx];
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
void rbfv1gridcalc3vrec(rbfv1model* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Integer */ ae_vector* blocks0,
|
|
ae_int_t block0a,
|
|
ae_int_t block0b,
|
|
/* Integer */ ae_vector* blocks1,
|
|
ae_int_t block1a,
|
|
ae_int_t block1b,
|
|
/* Integer */ ae_vector* blocks2,
|
|
ae_int_t block2a,
|
|
ae_int_t block2b,
|
|
/* Boolean */ ae_vector* flagy,
|
|
ae_bool sparsey,
|
|
double searchradius,
|
|
double avgfuncpernode,
|
|
ae_shared_pool* bufpool,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t t;
|
|
ae_int_t l;
|
|
ae_int_t i0;
|
|
ae_int_t i1;
|
|
ae_int_t i2;
|
|
ae_int_t ic;
|
|
gridcalc3v1buf *pbuf;
|
|
ae_smart_ptr _pbuf;
|
|
ae_int_t flag12dim1;
|
|
ae_int_t flag12dim2;
|
|
double problemcost;
|
|
ae_int_t maxbs;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
double v;
|
|
ae_int_t kc;
|
|
ae_int_t tg;
|
|
double rcur;
|
|
double rcur2;
|
|
double basisfuncval;
|
|
ae_int_t dstoffs;
|
|
ae_int_t srcoffs;
|
|
ae_int_t ubnd;
|
|
double w0;
|
|
double w1;
|
|
double w2;
|
|
ae_bool allnodes;
|
|
ae_bool somenodes;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&_pbuf, 0, sizeof(_pbuf));
|
|
ae_smart_ptr_init(&_pbuf, (void**)&pbuf, _state, ae_true);
|
|
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
|
|
/*
|
|
* Try to split large problem
|
|
*/
|
|
problemcost = (s->nl+1)*s->ny*2*(avgfuncpernode+1);
|
|
problemcost = problemcost*(blocks0->ptr.p_int[block0b]-blocks0->ptr.p_int[block0a]);
|
|
problemcost = problemcost*(blocks1->ptr.p_int[block1b]-blocks1->ptr.p_int[block1a]);
|
|
problemcost = problemcost*(blocks2->ptr.p_int[block2b]-blocks2->ptr.p_int[block2a]);
|
|
maxbs = 0;
|
|
maxbs = ae_maxint(maxbs, block0b-block0a, _state);
|
|
maxbs = ae_maxint(maxbs, block1b-block1a, _state);
|
|
maxbs = ae_maxint(maxbs, block2b-block2a, _state);
|
|
if( ae_fp_greater_eq(problemcost,rbfv1_minbasecasecost)&&maxbs>=2 )
|
|
{
|
|
if( block0b-block0a==maxbs )
|
|
{
|
|
rbfv1gridcalc3vrec(s, x0, n0, x1, n1, x2, n2, blocks0, block0a, block0a+maxbs/2, blocks1, block1a, block1b, blocks2, block2a, block2b, flagy, sparsey, searchradius, avgfuncpernode, bufpool, y, _state);
|
|
rbfv1gridcalc3vrec(s, x0, n0, x1, n1, x2, n2, blocks0, block0a+maxbs/2, block0b, blocks1, block1a, block1b, blocks2, block2a, block2b, flagy, sparsey, searchradius, avgfuncpernode, bufpool, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( block1b-block1a==maxbs )
|
|
{
|
|
rbfv1gridcalc3vrec(s, x0, n0, x1, n1, x2, n2, blocks0, block0a, block0b, blocks1, block1a, block1a+maxbs/2, blocks2, block2a, block2b, flagy, sparsey, searchradius, avgfuncpernode, bufpool, y, _state);
|
|
rbfv1gridcalc3vrec(s, x0, n0, x1, n1, x2, n2, blocks0, block0a, block0b, blocks1, block1a+maxbs/2, block1b, blocks2, block2a, block2b, flagy, sparsey, searchradius, avgfuncpernode, bufpool, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( block2b-block2a==maxbs )
|
|
{
|
|
rbfv1gridcalc3vrec(s, x0, n0, x1, n1, x2, n2, blocks0, block0a, block0b, blocks1, block1a, block1b, blocks2, block2a, block2a+maxbs/2, flagy, sparsey, searchradius, avgfuncpernode, bufpool, y, _state);
|
|
rbfv1gridcalc3vrec(s, x0, n0, x1, n1, x2, n2, blocks0, block0a, block0b, blocks1, block1a, block1b, blocks2, block2a+maxbs/2, block2b, flagy, sparsey, searchradius, avgfuncpernode, bufpool, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Retrieve buffer object from pool (it will be returned later)
|
|
*/
|
|
ae_shared_pool_retrieve(bufpool, &_pbuf, _state);
|
|
|
|
/*
|
|
* Calculate RBF model
|
|
*/
|
|
for(i2=block2a; i2<=block2b-1; i2++)
|
|
{
|
|
for(i1=block1a; i1<=block1b-1; i1++)
|
|
{
|
|
for(i0=block0a; i0<=block0b-1; i0++)
|
|
{
|
|
|
|
/*
|
|
* Analyze block - determine what elements are needed and what are not.
|
|
*
|
|
* After this block is done, two flag variables can be used:
|
|
* * SomeNodes, which is True when there are at least one node which have
|
|
* to be calculated
|
|
* * AllNodes, which is True when all nodes are required
|
|
*/
|
|
somenodes = ae_true;
|
|
allnodes = ae_true;
|
|
flag12dim1 = blocks1->ptr.p_int[i1+1]-blocks1->ptr.p_int[i1];
|
|
flag12dim2 = blocks2->ptr.p_int[i2+1]-blocks2->ptr.p_int[i2];
|
|
if( sparsey )
|
|
{
|
|
|
|
/*
|
|
* Use FlagY to determine what is required.
|
|
*/
|
|
bvectorsetlengthatleast(&pbuf->flag0, n0, _state);
|
|
bvectorsetlengthatleast(&pbuf->flag1, n1, _state);
|
|
bvectorsetlengthatleast(&pbuf->flag2, n2, _state);
|
|
bvectorsetlengthatleast(&pbuf->flag12, flag12dim1*flag12dim2, _state);
|
|
for(i=blocks0->ptr.p_int[i0]; i<=blocks0->ptr.p_int[i0+1]-1; i++)
|
|
{
|
|
pbuf->flag0.ptr.p_bool[i] = ae_false;
|
|
}
|
|
for(j=blocks1->ptr.p_int[i1]; j<=blocks1->ptr.p_int[i1+1]-1; j++)
|
|
{
|
|
pbuf->flag1.ptr.p_bool[j] = ae_false;
|
|
}
|
|
for(k=blocks2->ptr.p_int[i2]; k<=blocks2->ptr.p_int[i2+1]-1; k++)
|
|
{
|
|
pbuf->flag2.ptr.p_bool[k] = ae_false;
|
|
}
|
|
for(i=0; i<=flag12dim1*flag12dim2-1; i++)
|
|
{
|
|
pbuf->flag12.ptr.p_bool[i] = ae_false;
|
|
}
|
|
somenodes = ae_false;
|
|
allnodes = ae_true;
|
|
for(k=blocks2->ptr.p_int[i2]; k<=blocks2->ptr.p_int[i2+1]-1; k++)
|
|
{
|
|
for(j=blocks1->ptr.p_int[i1]; j<=blocks1->ptr.p_int[i1+1]-1; j++)
|
|
{
|
|
dstoffs = j-blocks1->ptr.p_int[i1]+flag12dim1*(k-blocks2->ptr.p_int[i2]);
|
|
srcoffs = j*n0+k*n0*n1;
|
|
for(i=blocks0->ptr.p_int[i0]; i<=blocks0->ptr.p_int[i0+1]-1; i++)
|
|
{
|
|
if( flagy->ptr.p_bool[srcoffs+i] )
|
|
{
|
|
pbuf->flag0.ptr.p_bool[i] = ae_true;
|
|
pbuf->flag1.ptr.p_bool[j] = ae_true;
|
|
pbuf->flag2.ptr.p_bool[k] = ae_true;
|
|
pbuf->flag12.ptr.p_bool[dstoffs] = ae_true;
|
|
somenodes = ae_true;
|
|
}
|
|
else
|
|
{
|
|
allnodes = ae_false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Skip block if it is completely empty.
|
|
*/
|
|
if( !somenodes )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* compute linear term for block (I0,I1,I2)
|
|
*/
|
|
for(k=blocks2->ptr.p_int[i2]; k<=blocks2->ptr.p_int[i2+1]-1; k++)
|
|
{
|
|
for(j=blocks1->ptr.p_int[i1]; j<=blocks1->ptr.p_int[i1+1]-1; j++)
|
|
{
|
|
|
|
/*
|
|
* do we need this micro-row?
|
|
*/
|
|
if( !allnodes&&!pbuf->flag12.ptr.p_bool[j-blocks1->ptr.p_int[i1]+flag12dim1*(k-blocks2->ptr.p_int[i2])] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Compute linear term
|
|
*/
|
|
for(i=blocks0->ptr.p_int[i0]; i<=blocks0->ptr.p_int[i0+1]-1; i++)
|
|
{
|
|
pbuf->tx.ptr.p_double[0] = x0->ptr.p_double[i];
|
|
pbuf->tx.ptr.p_double[1] = x1->ptr.p_double[j];
|
|
pbuf->tx.ptr.p_double[2] = x2->ptr.p_double[k];
|
|
for(l=0; l<=s->ny-1; l++)
|
|
{
|
|
v = s->v.ptr.pp_double[l][rbfv1_mxnx];
|
|
for(t=0; t<=nx-1; t++)
|
|
{
|
|
v = v+s->v.ptr.pp_double[l][t]*pbuf->tx.ptr.p_double[t];
|
|
}
|
|
y->ptr.p_double[l+ny*(i+j*n0+k*n0*n1)] = v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* compute RBF term for block (I0,I1,I2)
|
|
*/
|
|
pbuf->tx.ptr.p_double[0] = 0.5*(x0->ptr.p_double[blocks0->ptr.p_int[i0]]+x0->ptr.p_double[blocks0->ptr.p_int[i0+1]-1]);
|
|
pbuf->tx.ptr.p_double[1] = 0.5*(x1->ptr.p_double[blocks1->ptr.p_int[i1]]+x1->ptr.p_double[blocks1->ptr.p_int[i1+1]-1]);
|
|
pbuf->tx.ptr.p_double[2] = 0.5*(x2->ptr.p_double[blocks2->ptr.p_int[i2]]+x2->ptr.p_double[blocks2->ptr.p_int[i2+1]-1]);
|
|
kc = kdtreetsqueryrnn(&s->tree, &pbuf->requestbuf, &pbuf->tx, searchradius, ae_true, _state);
|
|
kdtreetsqueryresultsx(&s->tree, &pbuf->requestbuf, &pbuf->calcbufx, _state);
|
|
kdtreetsqueryresultstags(&s->tree, &pbuf->requestbuf, &pbuf->calcbuftags, _state);
|
|
for(ic=0; ic<=kc-1; ic++)
|
|
{
|
|
pbuf->cx.ptr.p_double[0] = pbuf->calcbufx.ptr.pp_double[ic][0];
|
|
pbuf->cx.ptr.p_double[1] = pbuf->calcbufx.ptr.pp_double[ic][1];
|
|
pbuf->cx.ptr.p_double[2] = pbuf->calcbufx.ptr.pp_double[ic][2];
|
|
tg = pbuf->calcbuftags.ptr.p_int[ic];
|
|
rcur = s->wr.ptr.pp_double[tg][0];
|
|
rcur2 = rcur*rcur;
|
|
for(i=blocks0->ptr.p_int[i0]; i<=blocks0->ptr.p_int[i0+1]-1; i++)
|
|
{
|
|
if( allnodes||pbuf->flag0.ptr.p_bool[i] )
|
|
{
|
|
pbuf->expbuf0.ptr.p_double[i] = ae_exp(-ae_sqr(x0->ptr.p_double[i]-pbuf->cx.ptr.p_double[0], _state)/rcur2, _state);
|
|
}
|
|
else
|
|
{
|
|
pbuf->expbuf0.ptr.p_double[i] = 0.0;
|
|
}
|
|
}
|
|
for(j=blocks1->ptr.p_int[i1]; j<=blocks1->ptr.p_int[i1+1]-1; j++)
|
|
{
|
|
if( allnodes||pbuf->flag1.ptr.p_bool[j] )
|
|
{
|
|
pbuf->expbuf1.ptr.p_double[j] = ae_exp(-ae_sqr(x1->ptr.p_double[j]-pbuf->cx.ptr.p_double[1], _state)/rcur2, _state);
|
|
}
|
|
else
|
|
{
|
|
pbuf->expbuf1.ptr.p_double[j] = 0.0;
|
|
}
|
|
}
|
|
for(k=blocks2->ptr.p_int[i2]; k<=blocks2->ptr.p_int[i2+1]-1; k++)
|
|
{
|
|
if( allnodes||pbuf->flag2.ptr.p_bool[k] )
|
|
{
|
|
pbuf->expbuf2.ptr.p_double[k] = ae_exp(-ae_sqr(x2->ptr.p_double[k]-pbuf->cx.ptr.p_double[2], _state)/rcur2, _state);
|
|
}
|
|
else
|
|
{
|
|
pbuf->expbuf2.ptr.p_double[k] = 0.0;
|
|
}
|
|
}
|
|
for(t=0; t<=s->nl-1; t++)
|
|
{
|
|
|
|
/*
|
|
* Calculate
|
|
*/
|
|
for(k=blocks2->ptr.p_int[i2]; k<=blocks2->ptr.p_int[i2+1]-1; k++)
|
|
{
|
|
for(j=blocks1->ptr.p_int[i1]; j<=blocks1->ptr.p_int[i1+1]-1; j++)
|
|
{
|
|
|
|
/*
|
|
* do we need this micro-row?
|
|
*/
|
|
if( !allnodes&&!pbuf->flag12.ptr.p_bool[j-blocks1->ptr.p_int[i1]+flag12dim1*(k-blocks2->ptr.p_int[i2])] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Prepare local variables
|
|
*/
|
|
dstoffs = ny*(blocks0->ptr.p_int[i0]+j*n0+k*n0*n1);
|
|
v = pbuf->expbuf1.ptr.p_double[j]*pbuf->expbuf2.ptr.p_double[k];
|
|
|
|
/*
|
|
* Optimized for NY=1
|
|
*/
|
|
if( s->ny==1 )
|
|
{
|
|
w0 = s->wr.ptr.pp_double[tg][1+t*s->ny+0];
|
|
ubnd = blocks0->ptr.p_int[i0+1]-1;
|
|
for(i=blocks0->ptr.p_int[i0]; i<=ubnd; i++)
|
|
{
|
|
basisfuncval = pbuf->expbuf0.ptr.p_double[i]*v;
|
|
y->ptr.p_double[dstoffs] = y->ptr.p_double[dstoffs]+basisfuncval*w0;
|
|
dstoffs = dstoffs+1;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Optimized for NY=2
|
|
*/
|
|
if( s->ny==2 )
|
|
{
|
|
w0 = s->wr.ptr.pp_double[tg][1+t*s->ny+0];
|
|
w1 = s->wr.ptr.pp_double[tg][1+t*s->ny+1];
|
|
ubnd = blocks0->ptr.p_int[i0+1]-1;
|
|
for(i=blocks0->ptr.p_int[i0]; i<=ubnd; i++)
|
|
{
|
|
basisfuncval = pbuf->expbuf0.ptr.p_double[i]*v;
|
|
y->ptr.p_double[dstoffs+0] = y->ptr.p_double[dstoffs+0]+basisfuncval*w0;
|
|
y->ptr.p_double[dstoffs+1] = y->ptr.p_double[dstoffs+1]+basisfuncval*w1;
|
|
dstoffs = dstoffs+2;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Optimized for NY=3
|
|
*/
|
|
if( s->ny==3 )
|
|
{
|
|
w0 = s->wr.ptr.pp_double[tg][1+t*s->ny+0];
|
|
w1 = s->wr.ptr.pp_double[tg][1+t*s->ny+1];
|
|
w2 = s->wr.ptr.pp_double[tg][1+t*s->ny+2];
|
|
ubnd = blocks0->ptr.p_int[i0+1]-1;
|
|
for(i=blocks0->ptr.p_int[i0]; i<=ubnd; i++)
|
|
{
|
|
basisfuncval = pbuf->expbuf0.ptr.p_double[i]*v;
|
|
y->ptr.p_double[dstoffs+0] = y->ptr.p_double[dstoffs+0]+basisfuncval*w0;
|
|
y->ptr.p_double[dstoffs+1] = y->ptr.p_double[dstoffs+1]+basisfuncval*w1;
|
|
y->ptr.p_double[dstoffs+2] = y->ptr.p_double[dstoffs+2]+basisfuncval*w2;
|
|
dstoffs = dstoffs+3;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* General case
|
|
*/
|
|
for(i=blocks0->ptr.p_int[i0]; i<=blocks0->ptr.p_int[i0+1]-1; i++)
|
|
{
|
|
basisfuncval = pbuf->expbuf0.ptr.p_double[i]*v;
|
|
for(l=0; l<=s->ny-1; l++)
|
|
{
|
|
y->ptr.p_double[l+dstoffs] = y->ptr.p_double[l+dstoffs]+basisfuncval*s->wr.ptr.pp_double[tg][1+t*s->ny+l];
|
|
}
|
|
dstoffs = dstoffs+ny;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Update basis functions
|
|
*/
|
|
if( t!=s->nl-1 )
|
|
{
|
|
ubnd = blocks0->ptr.p_int[i0+1]-1;
|
|
for(i=blocks0->ptr.p_int[i0]; i<=ubnd; i++)
|
|
{
|
|
if( allnodes||pbuf->flag0.ptr.p_bool[i] )
|
|
{
|
|
v = pbuf->expbuf0.ptr.p_double[i]*pbuf->expbuf0.ptr.p_double[i];
|
|
pbuf->expbuf0.ptr.p_double[i] = v*v;
|
|
}
|
|
}
|
|
ubnd = blocks1->ptr.p_int[i1+1]-1;
|
|
for(j=blocks1->ptr.p_int[i1]; j<=ubnd; j++)
|
|
{
|
|
if( allnodes||pbuf->flag1.ptr.p_bool[j] )
|
|
{
|
|
v = pbuf->expbuf1.ptr.p_double[j]*pbuf->expbuf1.ptr.p_double[j];
|
|
pbuf->expbuf1.ptr.p_double[j] = v*v;
|
|
}
|
|
}
|
|
ubnd = blocks2->ptr.p_int[i2+1]-1;
|
|
for(k=blocks2->ptr.p_int[i2]; k<=ubnd; k++)
|
|
{
|
|
if( allnodes||pbuf->flag2.ptr.p_bool[k] )
|
|
{
|
|
v = pbuf->expbuf2.ptr.p_double[k]*pbuf->expbuf2.ptr.p_double[k];
|
|
pbuf->expbuf2.ptr.p_double[k] = v*v;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Recycle buffer object back to pool
|
|
*/
|
|
ae_shared_pool_recycle(bufpool, &_pbuf, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Serial stub for GPL edition.
|
|
*************************************************************************/
|
|
ae_bool _trypexec_rbfv1gridcalc3vrec(rbfv1model* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Integer */ ae_vector* blocks0,
|
|
ae_int_t block0a,
|
|
ae_int_t block0b,
|
|
/* Integer */ ae_vector* blocks1,
|
|
ae_int_t block1a,
|
|
ae_int_t block1b,
|
|
/* Integer */ ae_vector* blocks2,
|
|
ae_int_t block2a,
|
|
ae_int_t block2b,
|
|
/* Boolean */ ae_vector* flagy,
|
|
ae_bool sparsey,
|
|
double searchradius,
|
|
double avgfuncpernode,
|
|
ae_shared_pool* bufpool,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
return ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 rbfv1unpack(rbfv1model* 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][rbfv1_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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static ae_bool rbfv1_rbfv1buildlinearmodel(/* 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);
|
|
memset(&tmpy, 0, sizeof(tmpy));
|
|
memset(&a, 0, sizeof(a));
|
|
memset(&shifting, 0, sizeof(shifting));
|
|
memset(&c, 0, sizeof(c));
|
|
memset(&rep, 0, sizeof(rep));
|
|
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, rbfv1_mxnx+1, _state);
|
|
if( n==0 )
|
|
{
|
|
for(j=0; j<=rbfv1_mxnx; j++)
|
|
{
|
|
for(i=0; i<=ny-1; i++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(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, rbfv1_mxnx+1, _state);
|
|
ae_vector_set_length(&shifting, rbfv1_mxnx, _state);
|
|
scaling = (double)(0);
|
|
for(i=0; i<=rbfv1_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,(double)(0)) )
|
|
{
|
|
scaling = (double)(1);
|
|
}
|
|
else
|
|
{
|
|
scaling = 0.5*scaling;
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
for(j=0; j<=rbfv1_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][rbfv1_mxnx] = (double)(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, rbfv1_mxnx+1, &info, &c, &rep, _state);
|
|
if( info<=0 )
|
|
{
|
|
result = ae_false;
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
for(j=0; j<=rbfv1_mxnx-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = c.ptr.p_double[j]/scaling;
|
|
}
|
|
v->ptr.pp_double[i][rbfv1_mxnx] = c.ptr.p_double[rbfv1_mxnx];
|
|
for(j=0; j<=rbfv1_mxnx-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][rbfv1_mxnx] = v->ptr.pp_double[i][rbfv1_mxnx]-shifting.ptr.p_double[j]*v->ptr.pp_double[i][j];
|
|
}
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
for(k=0; k<=rbfv1_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][rbfv1_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<=rbfv1_mxnx; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
for(j=0; j<=n-1; j++)
|
|
{
|
|
v->ptr.pp_double[i][rbfv1_mxnx] = v->ptr.pp_double[i][rbfv1_mxnx]+y->ptr.pp_double[j][i];
|
|
}
|
|
if( n>0 )
|
|
{
|
|
v->ptr.pp_double[i][rbfv1_mxnx] = v->ptr.pp_double[i][rbfv1_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][rbfv1_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<=rbfv1_mxnx; j++)
|
|
{
|
|
v->ptr.pp_double[i][j] = (double)(0);
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return result;
|
|
}
|
|
|
|
|
|
static void rbfv1_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);
|
|
memset(&state, 0, sizeof(state));
|
|
memset(&lsqrrep, 0, sizeof(lsqrrep));
|
|
memset(&spg, 0, sizeof(spg));
|
|
memset(&sps, 0, sizeof(sps));
|
|
memset(&nearcenterscnt, 0, sizeof(nearcenterscnt));
|
|
memset(&nearpointscnt, 0, sizeof(nearpointscnt));
|
|
memset(&skipnearpointscnt, 0, sizeof(skipnearpointscnt));
|
|
memset(&farpointscnt, 0, sizeof(farpointscnt));
|
|
memset(&pointstags, 0, sizeof(pointstags));
|
|
memset(¢erstags, 0, sizeof(centerstags));
|
|
memset(&nearpoints, 0, sizeof(nearpoints));
|
|
memset(&nearcenters, 0, sizeof(nearcenters));
|
|
memset(&farpoints, 0, sizeof(farpoints));
|
|
memset(&xcx, 0, sizeof(xcx));
|
|
memset(&tmpy, 0, sizeof(tmpy));
|
|
memset(&tc, 0, sizeof(tc));
|
|
memset(&g, 0, sizeof(g));
|
|
memset(&c, 0, sizeof(c));
|
|
memset(&a, 0, sizeof(a));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
memset(&tmp1, 0, sizeof(tmp1));
|
|
memset(&tmp2, 0, sizeof(tmp2));
|
|
memset(&xx, 0, sizeof(xx));
|
|
memset(&cx, 0, sizeof(cx));
|
|
*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, rbfv1_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<=rbfv1_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]*rbfv1_rbfnearradius, ae_true, _state);
|
|
kdtreequeryresultstags(centerstree, ¢erstags, _state);
|
|
maxrad = (double)(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)*rbfv1_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]*rbfv1_rbfnearradius+maxrad*rbfv1_rbffarradius, r->ptr.p_double[i]*rbfv1_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, rbfv1_mxnx, _state);
|
|
ae_matrix_set_length(&nearpoints, ae_maxint(maxnearpointscnt, 1, _state), rbfv1_mxnx, _state);
|
|
ae_matrix_set_length(&farpoints, ae_maxint(maxfarpointscnt, 1, _state), rbfv1_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<=rbfv1_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 = (double)(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, (double)(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 = (double)(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 rbfv1_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);
|
|
memset(&state, 0, sizeof(state));
|
|
memset(&lsqrrep, 0, sizeof(lsqrrep));
|
|
memset(&spa, 0, sizeof(spa));
|
|
memset(&omega, 0, sizeof(omega));
|
|
memset(&xx, 0, sizeof(xx));
|
|
memset(&tmpy, 0, sizeof(tmpy));
|
|
memset(&cx, 0, sizeof(cx));
|
|
memset(¢erstags, 0, sizeof(centerstags));
|
|
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(rbfv1_mxnx>0&&rbfv1_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, rbfv1_mxnx, _state);
|
|
ae_vector_set_length(¢erstags, n, _state);
|
|
ae_matrix_set_length(xc, *nc, rbfv1_mxnx, _state);
|
|
ae_vector_set_length(r, *nc, _state);
|
|
for(i=0; i<=*nc-1; i++)
|
|
{
|
|
for(j=0; j<=rbfv1_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((double)(2), (double)(i/n), _state);
|
|
}
|
|
for(i=0; i<=n-1; i++)
|
|
{
|
|
centerstags.ptr.p_int[i] = i;
|
|
}
|
|
kdtreebuildtagged(xc, ¢erstags, n, rbfv1_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<=rbfv1_mxnx-1; j++)
|
|
{
|
|
xx.ptr.p_double[j] = x->ptr.pp_double[i][j];
|
|
}
|
|
*annz = *annz+kdtreequeryrnn(centerstree, &xx, r->ptr.p_double[0]*rbfv1_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<=rbfv1_mxnx-1; j++)
|
|
{
|
|
xx.ptr.p_double[j] = x->ptr.pp_double[i][j];
|
|
}
|
|
nec = kdtreequeryrnn(centerstree, &xx, r->ptr.p_double[layer*n]*rbfv1_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 = (double)(0);
|
|
for(k=0; k<=rbfv1_mxnx-1; k++)
|
|
{
|
|
xx.ptr.p_double[k] = x->ptr.pp_double[j][k];
|
|
}
|
|
nec = kdtreequeryrnn(centerstree, &xx, r->ptr.p_double[layer*n]*rbfv1_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);
|
|
}
|
|
|
|
|
|
void _rbfv1calcbuffer_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv1calcbuffer *p = (rbfv1calcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->calcbufxcx, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->calcbufx, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->calcbuftags, 0, DT_INT, _state, make_automatic);
|
|
_kdtreerequestbuffer_init(&p->requestbuffer, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv1calcbuffer_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv1calcbuffer *dst = (rbfv1calcbuffer*)_dst;
|
|
rbfv1calcbuffer *src = (rbfv1calcbuffer*)_src;
|
|
ae_vector_init_copy(&dst->calcbufxcx, &src->calcbufxcx, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->calcbufx, &src->calcbufx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->calcbuftags, &src->calcbuftags, _state, make_automatic);
|
|
_kdtreerequestbuffer_init_copy(&dst->requestbuffer, &src->requestbuffer, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv1calcbuffer_clear(void* _p)
|
|
{
|
|
rbfv1calcbuffer *p = (rbfv1calcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->calcbufxcx);
|
|
ae_matrix_clear(&p->calcbufx);
|
|
ae_vector_clear(&p->calcbuftags);
|
|
_kdtreerequestbuffer_clear(&p->requestbuffer);
|
|
}
|
|
|
|
|
|
void _rbfv1calcbuffer_destroy(void* _p)
|
|
{
|
|
rbfv1calcbuffer *p = (rbfv1calcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->calcbufxcx);
|
|
ae_matrix_destroy(&p->calcbufx);
|
|
ae_vector_destroy(&p->calcbuftags);
|
|
_kdtreerequestbuffer_destroy(&p->requestbuffer);
|
|
}
|
|
|
|
|
|
void _rbfv1model_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv1model *p = (rbfv1model*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_kdtree_init(&p->tree, _state, make_automatic);
|
|
ae_matrix_init(&p->xc, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->wr, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->v, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->calcbufxcx, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->calcbufx, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->calcbuftags, 0, DT_INT, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv1model_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv1model *dst = (rbfv1model*)_dst;
|
|
rbfv1model *src = (rbfv1model*)_src;
|
|
dst->ny = src->ny;
|
|
dst->nx = src->nx;
|
|
dst->nc = src->nc;
|
|
dst->nl = src->nl;
|
|
_kdtree_init_copy(&dst->tree, &src->tree, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->xc, &src->xc, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->wr, &src->wr, _state, make_automatic);
|
|
dst->rmax = src->rmax;
|
|
ae_matrix_init_copy(&dst->v, &src->v, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->calcbufxcx, &src->calcbufxcx, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->calcbufx, &src->calcbufx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->calcbuftags, &src->calcbuftags, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfv1model_clear(void* _p)
|
|
{
|
|
rbfv1model *p = (rbfv1model*)_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_vector_clear(&p->calcbufxcx);
|
|
ae_matrix_clear(&p->calcbufx);
|
|
ae_vector_clear(&p->calcbuftags);
|
|
}
|
|
|
|
|
|
void _rbfv1model_destroy(void* _p)
|
|
{
|
|
rbfv1model *p = (rbfv1model*)_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_vector_destroy(&p->calcbufxcx);
|
|
ae_matrix_destroy(&p->calcbufx);
|
|
ae_vector_destroy(&p->calcbuftags);
|
|
}
|
|
|
|
|
|
void _gridcalc3v1buf_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
gridcalc3v1buf *p = (gridcalc3v1buf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_init(&p->tx, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->cx, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->ty, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->flag0, 0, DT_BOOL, _state, make_automatic);
|
|
ae_vector_init(&p->flag1, 0, DT_BOOL, _state, make_automatic);
|
|
ae_vector_init(&p->flag2, 0, DT_BOOL, _state, make_automatic);
|
|
ae_vector_init(&p->flag12, 0, DT_BOOL, _state, make_automatic);
|
|
ae_vector_init(&p->expbuf0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->expbuf1, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->expbuf2, 0, DT_REAL, _state, make_automatic);
|
|
_kdtreerequestbuffer_init(&p->requestbuf, _state, make_automatic);
|
|
ae_matrix_init(&p->calcbufx, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->calcbuftags, 0, DT_INT, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _gridcalc3v1buf_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
gridcalc3v1buf *dst = (gridcalc3v1buf*)_dst;
|
|
gridcalc3v1buf *src = (gridcalc3v1buf*)_src;
|
|
ae_vector_init_copy(&dst->tx, &src->tx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->cx, &src->cx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->ty, &src->ty, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->flag0, &src->flag0, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->flag1, &src->flag1, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->flag2, &src->flag2, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->flag12, &src->flag12, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->expbuf0, &src->expbuf0, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->expbuf1, &src->expbuf1, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->expbuf2, &src->expbuf2, _state, make_automatic);
|
|
_kdtreerequestbuffer_init_copy(&dst->requestbuf, &src->requestbuf, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->calcbufx, &src->calcbufx, _state, make_automatic);
|
|
ae_vector_init_copy(&dst->calcbuftags, &src->calcbuftags, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _gridcalc3v1buf_clear(void* _p)
|
|
{
|
|
gridcalc3v1buf *p = (gridcalc3v1buf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_clear(&p->tx);
|
|
ae_vector_clear(&p->cx);
|
|
ae_vector_clear(&p->ty);
|
|
ae_vector_clear(&p->flag0);
|
|
ae_vector_clear(&p->flag1);
|
|
ae_vector_clear(&p->flag2);
|
|
ae_vector_clear(&p->flag12);
|
|
ae_vector_clear(&p->expbuf0);
|
|
ae_vector_clear(&p->expbuf1);
|
|
ae_vector_clear(&p->expbuf2);
|
|
_kdtreerequestbuffer_clear(&p->requestbuf);
|
|
ae_matrix_clear(&p->calcbufx);
|
|
ae_vector_clear(&p->calcbuftags);
|
|
}
|
|
|
|
|
|
void _gridcalc3v1buf_destroy(void* _p)
|
|
{
|
|
gridcalc3v1buf *p = (gridcalc3v1buf*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
ae_vector_destroy(&p->tx);
|
|
ae_vector_destroy(&p->cx);
|
|
ae_vector_destroy(&p->ty);
|
|
ae_vector_destroy(&p->flag0);
|
|
ae_vector_destroy(&p->flag1);
|
|
ae_vector_destroy(&p->flag2);
|
|
ae_vector_destroy(&p->flag12);
|
|
ae_vector_destroy(&p->expbuf0);
|
|
ae_vector_destroy(&p->expbuf1);
|
|
ae_vector_destroy(&p->expbuf2);
|
|
_kdtreerequestbuffer_destroy(&p->requestbuf);
|
|
ae_matrix_destroy(&p->calcbufx);
|
|
ae_vector_destroy(&p->calcbuftags);
|
|
}
|
|
|
|
|
|
void _rbfv1report_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv1report *p = (rbfv1report*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _rbfv1report_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfv1report *dst = (rbfv1report*)_dst;
|
|
rbfv1report *src = (rbfv1report*)_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;
|
|
}
|
|
|
|
|
|
void _rbfv1report_clear(void* _p)
|
|
{
|
|
rbfv1report *p = (rbfv1report*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _rbfv1report_destroy(void* _p)
|
|
{
|
|
rbfv1report *p = (rbfv1report*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_RBF) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
This function creates RBF model for a scalar (NY=1) or vector (NY>1)
|
|
function in a NX-dimensional space (NX>=1).
|
|
|
|
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) or rbfsetpointsandscales().
|
|
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 tweaks algorithm properties with rbfsetalgohierarchical() method
|
|
(or chooses one of the legacy algorithms - 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.
|
|
|
|
IMPORTANT: we recommend you to use latest model construction algorithm -
|
|
hierarchical RBFs, which is activated by rbfsetalgohierarchical()
|
|
function. This algorithm is the fastest one, and most memory-
|
|
efficient.
|
|
However, it is incompatible with older versions of ALGLIB
|
|
(pre-3.11). So, if you serialize hierarchical model, you will
|
|
be unable to load it in pre-3.11 ALGLIB. Other model types (QNN
|
|
and RBF-ML) are still backward-compatible.
|
|
|
|
INPUT PARAMETERS:
|
|
NX - dimension of the space, NX>=1
|
|
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. Some additional memory
|
|
is allocated during model construction, but most of this memory is
|
|
freed after model coefficients are calculated. Amount of this
|
|
additional memory depends on model construction algorithm being
|
|
used.
|
|
|
|
NOTE 2: prior to ALGLIB version 3.11, RBF models supported only NX=2 or
|
|
NX=3. Any attempt to create single-dimensional or more than
|
|
3-dimensional RBF model resulted in exception.
|
|
|
|
ALGLIB 3.11 supports any NX>0, but models created with NX!=2 and
|
|
NX!=3 are incompatible with (a) older versions of ALGLIB, (b) old
|
|
model construction algorithms (QNN or RBF-ML).
|
|
|
|
So, if you create a model with NX=2 or NX=3, then, depending on
|
|
specific model construction algorithm being chosen, you will (QNN
|
|
and RBF-ML) or will not (HierarchicalRBF) get backward compatibility
|
|
with older versions of ALGLIB. You have a choice here.
|
|
|
|
However, if you create a model with NX neither 2 nor 3, you have
|
|
no backward compatibility from the start, and you are forced to
|
|
use hierarchical RBFs and ALGLIB 3.11 or later.
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011, 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfcreate(ae_int_t nx, ae_int_t ny, rbfmodel* s, ae_state *_state)
|
|
{
|
|
|
|
_rbfmodel_clear(s);
|
|
|
|
ae_assert(nx>=1, "RBFCreate: NX<1", _state);
|
|
ae_assert(ny>=1, "RBFCreate: NY<1", _state);
|
|
s->nx = nx;
|
|
s->ny = ny;
|
|
rbf_rbfpreparenonserializablefields(s, _state);
|
|
|
|
/*
|
|
* Select default model version according to NX.
|
|
*
|
|
* The idea is that when we call this function with NX=2 or NX=3, backward
|
|
* compatible dummy (zero) V1 model is created, so serialization produces
|
|
* model which are compatible with pre-3.11 ALGLIB.
|
|
*/
|
|
rbf_initializev1(nx, ny, &s->model1, _state);
|
|
rbf_initializev2(nx, ny, &s->model2, _state);
|
|
if( nx==2||nx==3 )
|
|
{
|
|
s->modelversion = 1;
|
|
}
|
|
else
|
|
{
|
|
s->modelversion = 2;
|
|
}
|
|
|
|
/*
|
|
* Report fields
|
|
*/
|
|
s->progress10000 = 0;
|
|
s->terminationrequest = ae_false;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function creates buffer structure which can be used to perform
|
|
parallel RBF model evaluations (with one RBF model instance being
|
|
used from multiple threads, as long as different threads use different
|
|
instances of buffer).
|
|
|
|
This buffer object can be used with rbftscalcbuf() function (here "ts"
|
|
stands for "thread-safe", "buf" is a suffix which denotes function which
|
|
reuses previously allocated output space).
|
|
|
|
How to use it:
|
|
* create RBF model structure with rbfcreate()
|
|
* load data, tune parameters
|
|
* call rbfbuildmodel()
|
|
* call rbfcreatecalcbuffer(), once per thread working with RBF model (you
|
|
should call this function only AFTER call to rbfbuildmodel(), see below
|
|
for more information)
|
|
* call rbftscalcbuf() from different threads, with each thread working
|
|
with its own copy of buffer object.
|
|
|
|
INPUT PARAMETERS
|
|
S - RBF model
|
|
|
|
OUTPUT PARAMETERS
|
|
Buf - external buffer.
|
|
|
|
|
|
IMPORTANT: buffer object should be used only with RBF model object which
|
|
was used to initialize buffer. Any attempt to use buffer with
|
|
different object is dangerous - you may get memory violation
|
|
error because sizes of internal arrays do not fit to dimensions
|
|
of RBF structure.
|
|
|
|
IMPORTANT: you should call this function only for model which was built
|
|
with rbfbuildmodel() function, after successful invocation of
|
|
rbfbuildmodel(). Sizes of some internal structures are
|
|
determined only after model is built, so buffer object created
|
|
before model construction stage will be useless (and any
|
|
attempt to use it will result in exception).
|
|
|
|
-- ALGLIB --
|
|
Copyright 02.04.2016 by Sergey Bochkanov
|
|
*************************************************************************/
|
|
void rbfcreatecalcbuffer(rbfmodel* s,
|
|
rbfcalcbuffer* buf,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_rbfcalcbuffer_clear(buf);
|
|
|
|
if( s->modelversion==1 )
|
|
{
|
|
buf->modelversion = 1;
|
|
rbfv1createcalcbuffer(&s->model1, &buf->bufv1, _state);
|
|
return;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
buf->modelversion = 2;
|
|
rbfv2createcalcbuffer(&s->model2, &buf->bufv2, _state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "RBFCreateCalcBuffer: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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.
|
|
|
|
IMPORTANT: ALGLIB version 3.11 and later allows you to specify a set of
|
|
per-dimension scales. Interpolation radii are multiplied by the
|
|
scale vector. It may be useful if you have mixed spatio-temporal
|
|
data (say, a set of 3D slices recorded at different times).
|
|
You should call rbfsetpointsandscales() function to use this
|
|
feature.
|
|
|
|
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
|
|
specified, 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: dataset added by this function is not saved during model serialization.
|
|
MODEL ITSELF is serialized, but data used to build it are not.
|
|
|
|
So, if you 1) add dataset to empty RBF model, 2) serialize and
|
|
unserialize it, then you will get an empty RBF model with no dataset
|
|
being attached.
|
|
|
|
From the other side, if you call rbfbuildmodel() between (1) and (2),
|
|
then after (2) you will get your fully constructed RBF model - but
|
|
again with no dataset attached, so subsequent calls to rbfbuildmodel()
|
|
will produce empty model.
|
|
|
|
|
|
-- 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);
|
|
ae_assert(apservisfinitematrix(xy, n, s->nx+s->ny, _state), "RBFSetPoints: XY contains infinite or NaN values!", _state);
|
|
s->n = n;
|
|
s->hasscale = ae_false;
|
|
ae_matrix_set_length(&s->x, s->n, s->nx, _state);
|
|
ae_matrix_set_length(&s->y, s->n, s->ny, _state);
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
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 adds dataset and a vector of per-dimension scales.
|
|
|
|
It may be useful if you have mixed spatio-temporal data - say, a set of 3D
|
|
slices recorded at different times. Such data typically require different
|
|
RBF radii for spatial and temporal dimensions. ALGLIB solves this problem
|
|
by specifying single RBF radius, which is (optionally) multiplied by the
|
|
scale vector.
|
|
|
|
This function overrides results of the previous calls, i.e. multiple calls
|
|
of this function will result in only the last set being added.
|
|
|
|
IMPORTANT: only HierarchicalRBF algorithm can work with scaled points. So,
|
|
using this function results in RBF models which can be used in
|
|
ALGLIB 3.11 or later. Previous versions of the library will be
|
|
unable to unserialize models produced by HierarchicalRBF algo.
|
|
|
|
Any attempt to use this function with RBF-ML or QNN algorithms
|
|
will result in -3 error code being returned (incorrect
|
|
algorithm).
|
|
|
|
INPUT PARAMETERS:
|
|
R - 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
|
|
specified, in this case only leading [N,NX+NY] elements
|
|
will be used.
|
|
N - number of points in the dataset
|
|
S - array[NX], scale vector, S[i]>0.
|
|
|
|
After you've added dataset and (optionally) tuned algorithm settings you
|
|
should call rbfbuildmodel() in order to build a model for you.
|
|
|
|
NOTE: dataset added by this function is not saved during model serialization.
|
|
MODEL ITSELF is serialized, but data used to build it are not.
|
|
|
|
So, if you 1) add dataset to empty RBF model, 2) serialize and
|
|
unserialize it, then you will get an empty RBF model with no dataset
|
|
being attached.
|
|
|
|
From the other side, if you call rbfbuildmodel() between (1) and (2),
|
|
then after (2) you will get your fully constructed RBF model - but
|
|
again with no dataset attached, so subsequent calls to rbfbuildmodel()
|
|
will produce empty model.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetpointsandscales(rbfmodel* r,
|
|
/* Real */ ae_matrix* xy,
|
|
ae_int_t n,
|
|
/* Real */ ae_vector* s,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
|
|
|
|
ae_assert(n>0, "RBFSetPointsAndScales: N<0", _state);
|
|
ae_assert(xy->rows>=n, "RBFSetPointsAndScales: Rows(XY)<N", _state);
|
|
ae_assert(xy->cols>=r->nx+r->ny, "RBFSetPointsAndScales: Cols(XY)<NX+NY", _state);
|
|
ae_assert(s->cnt>=r->nx, "RBFSetPointsAndScales: Length(S)<NX", _state);
|
|
r->n = n;
|
|
r->hasscale = ae_true;
|
|
ae_matrix_set_length(&r->x, r->n, r->nx, _state);
|
|
ae_matrix_set_length(&r->y, r->n, r->ny, _state);
|
|
for(i=0; i<=r->n-1; i++)
|
|
{
|
|
for(j=0; j<=r->nx-1; j++)
|
|
{
|
|
r->x.ptr.pp_double[i][j] = xy->ptr.pp_double[i][j];
|
|
}
|
|
for(j=0; j<=r->ny-1; j++)
|
|
{
|
|
r->y.ptr.pp_double[i][j] = xy->ptr.pp_double[i][j+r->nx];
|
|
}
|
|
}
|
|
ae_vector_set_length(&r->s, r->nx, _state);
|
|
for(i=0; i<=r->nx-1; i++)
|
|
{
|
|
ae_assert(ae_isfinite(s->ptr.p_double[i], _state), "RBFSetPointsAndScales: S[i] is not finite number", _state);
|
|
ae_assert(ae_fp_greater(s->ptr.p_double[i],(double)(0)), "RBFSetPointsAndScales: S[i]<=0", _state);
|
|
r->s.ptr.p_double[i] = s->ptr.p_double[i];
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
DEPRECATED:since version 3.11 ALGLIB includes new RBF model construction
|
|
algorithm, Hierarchical RBF. This algorithm is faster and
|
|
requires less memory than QNN and RBF-ML. It is especially good
|
|
for large-scale interpolation problems. So, we recommend you to
|
|
consider Hierarchical RBF as default option.
|
|
|
|
==========================================================================
|
|
|
|
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,(double)(0)), "RBFSetAlgoQNN: Q<=0", _state);
|
|
ae_assert(ae_isfinite(z, _state), "RBFSetAlgoQNN: Z is infinite or NAN", _state);
|
|
ae_assert(ae_fp_greater(z,(double)(0)), "RBFSetAlgoQNN: Z<=0", _state);
|
|
s->radvalue = q;
|
|
s->radzvalue = z;
|
|
s->algorithmtype = 1;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
DEPRECATED:since version 3.11 ALGLIB includes new RBF model construction
|
|
algorithm, Hierarchical RBF. This algorithm is faster and
|
|
requires less memory than QNN and RBF-ML. It is especially good
|
|
for large-scale interpolation problems. So, we recommend you to
|
|
consider Hierarchical RBF as default option.
|
|
|
|
==========================================================================
|
|
|
|
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,(double)(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,(double)(0)), "RBFSetAlgoMultiLayer: LambdaV<0", _state);
|
|
s->radvalue = rbase;
|
|
s->nlayers = nlayers;
|
|
s->algorithmtype = 2;
|
|
s->lambdav = lambdav;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets RBF interpolation algorithm. ALGLIB supports several
|
|
RBF algorithms with different properties.
|
|
|
|
This algorithm is called Hierarchical RBF. It similar to its previous
|
|
incarnation, RBF-ML, i.e. it also builds a sequence of models with
|
|
decreasing radii. However, it uses more economical way of building upper
|
|
layers (ones with large radii), which results in faster model construction
|
|
and evaluation, as well as smaller memory footprint during construction.
|
|
|
|
This algorithm has following important features:
|
|
* ability to handle millions of points
|
|
* controllable smoothing via nonlinearity penalization
|
|
* support for NX-dimensional models with NX=1 or NX>3 (unlike QNN or RBF-ML)
|
|
* support for specification of per-dimensional radii via scale vector,
|
|
which is set by means of rbfsetpointsandscales() function. This feature
|
|
is useful if you solve spatio-temporal interpolation problems, where
|
|
different radii are required for spatial and temporal dimensions.
|
|
|
|
Running times are roughly proportional to:
|
|
* N*log(N)*NLayers - for model construction
|
|
* N*NLayers - for model evaluation
|
|
You may see that running time does not depend on search radius or points
|
|
density, just on number of layers in the hierarchy.
|
|
|
|
IMPORTANT: this model construction algorithm was introduced in ALGLIB 3.11
|
|
and produces models which are INCOMPATIBLE with previous
|
|
versions of ALGLIB. You can not unserialize models produced
|
|
with this function in ALGLIB 3.10 or earlier.
|
|
|
|
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.
|
|
LambdaNS- >=0, nonlinearity penalty coefficient, negative values are
|
|
not allowed. This parameter adds controllable smoothing to
|
|
the problem, which may reduce noise. Specification of non-
|
|
zero lambda means that in addition to fitting error solver
|
|
will also minimize LambdaNS*|S''(x)|^2 (appropriately
|
|
generalized to multiple dimensions.
|
|
|
|
Specification of exactly zero value means that no penalty
|
|
is added (we do not even evaluate matrix of second
|
|
derivatives which is necessary for smoothing).
|
|
|
|
Calculation of nonlinearity penalty is costly - it results
|
|
in several-fold increase of model construction time.
|
|
Evaluation time remains the same.
|
|
|
|
Optimal lambda is problem-dependent and requires trial
|
|
and error. Good value to start from is 1e-5...1e-6,
|
|
which corresponds to slightly noticeable smoothing of the
|
|
function. Value 1e-2 usually means that quite heavy
|
|
smoothing is applied.
|
|
|
|
TUNING ALGORITHM
|
|
|
|
In order to use this algorithm you have to choose three parameters:
|
|
* initial radius RBase
|
|
* number of layers in the model NLayers
|
|
* penalty coefficient LambdaNS
|
|
|
|
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 (or specify
|
|
non-zero LambdaNS).
|
|
|
|
TYPICAL ERRORS
|
|
|
|
1. 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.
|
|
|
|
2. 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.
|
|
|
|
-- ALGLIB --
|
|
Copyright 20.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetalgohierarchical(rbfmodel* s,
|
|
double rbase,
|
|
ae_int_t nlayers,
|
|
double lambdans,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(rbase, _state), "RBFSetAlgoHierarchical: RBase is infinite or NaN", _state);
|
|
ae_assert(ae_fp_greater(rbase,(double)(0)), "RBFSetAlgoHierarchical: RBase<=0", _state);
|
|
ae_assert(nlayers>=0, "RBFSetAlgoHierarchical: NLayers<0", _state);
|
|
ae_assert(ae_isfinite(lambdans, _state)&&ae_fp_greater_eq(lambdans,(double)(0)), "RBFSetAlgoHierarchical: LambdaNS<0 or infinite", _state);
|
|
s->radvalue = rbase;
|
|
s->nlayers = nlayers;
|
|
s->algorithmtype = 3;
|
|
s->lambdav = lambdans;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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 basis function type, which can be:
|
|
* 0 for classic Gaussian
|
|
* 1 for fast and compact bell-like basis function, which becomes exactly
|
|
zero at distance equal to 3*R (default option).
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
BF - basis function type:
|
|
* 0 - classic Gaussian
|
|
* 1 - fast and compact one
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetv2bf(rbfmodel* s, ae_int_t bf, ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(bf==0||bf==1, "RBFSetV2Its: BF<>0 and BF<>1", _state);
|
|
s->model2.basisfunction = bf;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets stopping criteria of the underlying linear solver for
|
|
hierarchical (version 2) RBF constructor.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
MaxIts - this criterion will stop algorithm after MaxIts iterations.
|
|
Typically a few hundreds iterations is required, with 400
|
|
being a good default value to start experimentation.
|
|
Zero value means that default value will be selected.
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetv2its(rbfmodel* s, ae_int_t maxits, ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(maxits>=0, "RBFSetV2Its: MaxIts is negative", _state);
|
|
s->model2.maxits = maxits;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function sets support radius parameter of hierarchical (version 2)
|
|
RBF constructor.
|
|
|
|
Hierarchical RBF model achieves great speed-up by removing from the model
|
|
excessive (too dense) nodes. Say, if you have RBF radius equal to 1 meter,
|
|
and two nodes are just 1 millimeter apart, you may remove one of them
|
|
without reducing model quality.
|
|
|
|
Support radius parameter is used to justify which points need removal, and
|
|
which do not. If two points are less than SUPPORT_R*CUR_RADIUS units of
|
|
distance apart, one of them is removed from the model. The larger support
|
|
radius is, the faster model construction AND evaluation are. However,
|
|
too large values result in "bumpy" models.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, initialized by RBFCreate() call
|
|
R - support radius coefficient, >=0.
|
|
Recommended values are [0.1,0.4] range, with 0.1 being
|
|
default value.
|
|
|
|
-- ALGLIB --
|
|
Copyright 01.02.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfsetv2supportr(rbfmodel* s, double r, ae_state *_state)
|
|
{
|
|
|
|
|
|
ae_assert(ae_isfinite(r, _state), "RBFSetV2SupportR: R is not finite", _state);
|
|
ae_assert(ae_fp_greater_eq(r,(double)(0)), "RBFSetV2SupportR: R<0", _state);
|
|
s->model2.supportr = r;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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,(double)(0)), "RBFSetCond: EpsOrt is negative, INF or NAN", _state);
|
|
ae_assert(ae_isfinite(epserr, _state)&&ae_fp_greater_eq(epserr,(double)(0)), "RBFSetCond: EpsB is negative, INF or NAN", _state);
|
|
ae_assert(maxits>=0, "RBFSetCond: MaxIts is negative", _state);
|
|
if( (ae_fp_eq(epsort,(double)(0))&&ae_fp_eq(epserr,(double)(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; only QNN returns this
|
|
error code, other algorithms can handle non-
|
|
distinct nodes.
|
|
* -4 - nonconvergence of the internal SVD solver
|
|
* -3 incorrect model construction algorithm was chosen:
|
|
QNN or RBF-ML, combined with one of the incompatible
|
|
features - NX=1 or NX>3; points with per-dimension
|
|
scales.
|
|
* 1 - successful termination
|
|
* 8 - a termination request was submitted via
|
|
rbfrequesttermination() function.
|
|
|
|
Fields which are set only by modern RBF solvers (hierarchical
|
|
or nonnegative; older solvers like QNN and ML initialize these
|
|
fields by NANs):
|
|
* rep.rmserror - root-mean-square error at nodes
|
|
* rep.maxerror - maximum error at nodes
|
|
|
|
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;
|
|
rbfv1report rep1;
|
|
rbfv2report rep2;
|
|
ae_matrix x3;
|
|
ae_vector scalevec;
|
|
ae_int_t i;
|
|
ae_int_t curalgorithmtype;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&rep1, 0, sizeof(rep1));
|
|
memset(&rep2, 0, sizeof(rep2));
|
|
memset(&x3, 0, sizeof(x3));
|
|
memset(&scalevec, 0, sizeof(scalevec));
|
|
_rbfreport_clear(rep);
|
|
_rbfv1report_init(&rep1, _state, ae_true);
|
|
_rbfv2report_init(&rep2, _state, ae_true);
|
|
ae_matrix_init(&x3, 0, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&scalevec, 0, DT_REAL, _state, ae_true);
|
|
|
|
|
|
/*
|
|
* Clean fields prior to processing
|
|
*/
|
|
rbf_clearreportfields(rep, _state);
|
|
s->progress10000 = 0;
|
|
s->terminationrequest = ae_false;
|
|
|
|
/*
|
|
* Autoselect algorithm
|
|
*/
|
|
if( s->algorithmtype==0 )
|
|
{
|
|
if( (s->nx<2||s->nx>3)||s->hasscale )
|
|
{
|
|
curalgorithmtype = 3;
|
|
}
|
|
else
|
|
{
|
|
curalgorithmtype = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
curalgorithmtype = s->algorithmtype;
|
|
}
|
|
|
|
/*
|
|
* Algorithms which generate V1 models
|
|
*/
|
|
if( curalgorithmtype==1||curalgorithmtype==2 )
|
|
{
|
|
|
|
/*
|
|
* Perform compatibility checks
|
|
*/
|
|
if( (s->nx<2||s->nx>3)||s->hasscale )
|
|
{
|
|
rep->terminationtype = -3;
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Try to build model.
|
|
*
|
|
* NOTE: due to historical reasons RBFV1BuildModel() accepts points
|
|
* cast to 3-dimensional space, even if they are really 2-dimensional.
|
|
* So, for 2D data we have to explicitly convert them to 3D.
|
|
*/
|
|
if( s->nx==2 )
|
|
{
|
|
|
|
/*
|
|
* Convert data to 3D
|
|
*/
|
|
rmatrixsetlengthatleast(&x3, s->n, 3, _state);
|
|
for(i=0; i<=s->n-1; i++)
|
|
{
|
|
x3.ptr.pp_double[i][0] = s->x.ptr.pp_double[i][0];
|
|
x3.ptr.pp_double[i][1] = s->x.ptr.pp_double[i][1];
|
|
x3.ptr.pp_double[i][2] = (double)(0);
|
|
}
|
|
rbfv1buildmodel(&x3, &s->y, s->n, s->aterm, curalgorithmtype, s->nlayers, s->radvalue, s->radzvalue, s->lambdav, s->epsort, s->epserr, s->maxits, &s->model1, &rep1, _state);
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* Work with raw data
|
|
*/
|
|
rbfv1buildmodel(&s->x, &s->y, s->n, s->aterm, curalgorithmtype, s->nlayers, s->radvalue, s->radzvalue, s->lambdav, s->epsort, s->epserr, s->maxits, &s->model1, &rep1, _state);
|
|
}
|
|
s->modelversion = 1;
|
|
|
|
/*
|
|
* Convert report fields
|
|
*/
|
|
rep->arows = rep1.arows;
|
|
rep->acols = rep1.acols;
|
|
rep->annz = rep1.annz;
|
|
rep->iterationscount = rep1.iterationscount;
|
|
rep->nmv = rep1.nmv;
|
|
rep->terminationtype = rep1.terminationtype;
|
|
|
|
/*
|
|
* Done
|
|
*/
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Algorithms which generate V2 models
|
|
*/
|
|
if( curalgorithmtype==3 )
|
|
{
|
|
|
|
/*
|
|
* Prepare scale vector - use unit values or user supplied ones
|
|
*/
|
|
ae_vector_set_length(&scalevec, s->nx, _state);
|
|
for(i=0; i<=s->nx-1; i++)
|
|
{
|
|
if( s->hasscale )
|
|
{
|
|
scalevec.ptr.p_double[i] = s->s.ptr.p_double[i];
|
|
}
|
|
else
|
|
{
|
|
scalevec.ptr.p_double[i] = (double)(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Build model
|
|
*/
|
|
rbfv2buildhierarchical(&s->x, &s->y, s->n, &scalevec, s->aterm, s->nlayers, s->radvalue, s->lambdav, &s->model2, &s->progress10000, &s->terminationrequest, &rep2, _state);
|
|
s->modelversion = 2;
|
|
|
|
/*
|
|
* Convert report fields
|
|
*/
|
|
rep->terminationtype = rep2.terminationtype;
|
|
rep->rmserror = rep2.rmserror;
|
|
rep->maxerror = rep2.maxerror;
|
|
|
|
/*
|
|
* Done
|
|
*/
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Critical error
|
|
*/
|
|
ae_assert(ae_false, "RBFBuildModel: integrity check failure", _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model in the given point.
|
|
|
|
IMPORTANT: this function works only with modern (hierarchical) RBFs. It
|
|
can not be used with legacy (version 1) RBFs because older RBF
|
|
code does not support 1-dimensional models.
|
|
|
|
This function should be used when we have NY=1 (scalar function) and NX=1
|
|
(1-dimensional space). If you have 3-dimensional space, use rbfcalc3(). If
|
|
you have 2-dimensional space, use rbfcalc3(). If you have general
|
|
situation (NX-dimensional space, NY-dimensional function) you should use
|
|
generic rbfcalc().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>1
|
|
* NY<>1
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
X0 - X-coordinate, finite number
|
|
|
|
RESULT:
|
|
value of the model or 0.0 (as defined above)
|
|
|
|
-- ALGLIB --
|
|
Copyright 13.12.2011 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfcalc1(rbfmodel* s, double x0, ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
ae_assert(ae_isfinite(x0, _state), "RBFCalc1: invalid value for X0 (X0 is Inf)!", _state);
|
|
result = (double)(0);
|
|
if( s->ny!=1||s->nx!=1 )
|
|
{
|
|
return result;
|
|
}
|
|
if( s->modelversion==1 )
|
|
{
|
|
result = (double)(0);
|
|
return result;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
result = rbfv2calc1(&s->model2, x0, _state);
|
|
return result;
|
|
}
|
|
ae_assert(ae_false, "RBFCalc1: integrity check failed", _state);
|
|
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=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 generic rbfcalc().
|
|
|
|
If you want to calculate function values many times, consider using
|
|
rbfgridcalc2v(), which is far more efficient than many subsequent calls to
|
|
rbfcalc2().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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)
|
|
{
|
|
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);
|
|
result = (double)(0);
|
|
if( s->ny!=1||s->nx!=2 )
|
|
{
|
|
return result;
|
|
}
|
|
if( s->modelversion==1 )
|
|
{
|
|
result = rbfv1calc2(&s->model1, x0, x1, _state);
|
|
return result;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
result = rbfv2calc2(&s->model2, x0, x1, _state);
|
|
return result;
|
|
}
|
|
ae_assert(ae_false, "RBFCalc2: integrity check failed", _state);
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates value 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 generic rbfcalc().
|
|
|
|
If you want to calculate function values many times, consider using
|
|
rbfgridcalc3v(), which is far more efficient than many subsequent calls to
|
|
rbfcalc3().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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)
|
|
{
|
|
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);
|
|
result = (double)(0);
|
|
if( s->ny!=1||s->nx!=3 )
|
|
{
|
|
return result;
|
|
}
|
|
if( s->modelversion==1 )
|
|
{
|
|
result = rbfv1calc3(&s->model1, x0, x1, x2, _state);
|
|
return result;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
result = rbfv2calc3(&s->model2, x0, x1, x2, _state);
|
|
return result;
|
|
}
|
|
ae_assert(ae_false, "RBFCalc3: integrity check failed", _state);
|
|
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().
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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.
|
|
|
|
If you want to perform parallel model evaluation from multiple threads,
|
|
use rbftscalcbuf() with per-thread buffer object.
|
|
|
|
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_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] = (double)(0);
|
|
}
|
|
if( s->modelversion==1 )
|
|
{
|
|
rbfv1calcbuf(&s->model1, x, y, _state);
|
|
return;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
rbfv2calcbuf(&s->model2, x, y, _state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "RBFCalcBuf: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the given point, using
|
|
external buffer object (internal temporaries of RBF model are not
|
|
modified).
|
|
|
|
This function allows to use same RBF model object in different threads,
|
|
assuming that different threads use different instances of buffer
|
|
structure.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, may be shared between different threads
|
|
Buf - buffer object created for this particular instance of RBF
|
|
model with rbfcreatecalcbuffer().
|
|
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 rbftscalcbuf(rbfmodel* s,
|
|
rbfcalcbuffer* buf,
|
|
/* Real */ ae_vector* x,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
|
|
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);
|
|
ae_assert(s->modelversion==buf->modelversion, "RBFCalcBuf: buffer object is not compatible with RBF model", _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] = (double)(0);
|
|
}
|
|
if( s->modelversion==1 )
|
|
{
|
|
rbfv1tscalcbuf(&s->model1, &buf->bufv1, x, y, _state);
|
|
return;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
rbfv2tscalcbuf(&s->model2, &buf->bufv2, x, y, _state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "RBFTsCalcBuf: integrity check failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This is legacy function for gridded calculation of RBF model.
|
|
|
|
It is superseded by rbfgridcalc2v() and rbfgridcalc2vsubset() functions.
|
|
|
|
-- 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;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&cpx0, 0, sizeof(cpx0));
|
|
memset(&cpx1, 0, sizeof(cpx1));
|
|
memset(&p01, 0, sizeof(p01));
|
|
memset(&p11, 0, sizeof(p11));
|
|
memset(&p2, 0, sizeof(p2));
|
|
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);
|
|
if( s->modelversion==1 )
|
|
{
|
|
rbfv1gridcalc2(&s->model1, x0, n0, x1, n1, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
rbfv2gridcalc2(&s->model2, x0, n0, x1, n1, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "RBFGridCalc2: integrity check failed", _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the regular grid,
|
|
which has N0*N1 points, with Point[I,J] = (X0[I], X1[J]). Vector-valued
|
|
RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1], where NY is a number of
|
|
"output" vector values (this function supports vector-
|
|
valued RBF models). Y is out-variable and is reallocated
|
|
by this function.
|
|
Y[K+NY*(I0+I1*N0)]=F_k(X0[I0],X1[I1]), for:
|
|
* K=0...NY-1
|
|
* I0=0...N0-1
|
|
* I1=0...N1-1
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
NOTE: if you need function values on some subset of regular grid, which
|
|
may be described as "several compact and dense islands", you may
|
|
use rbfgridcalc2vsubset().
|
|
|
|
-- ALGLIB --
|
|
Copyright 27.01.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc2v(rbfmodel* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector dummy;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&dummy, 0, sizeof(dummy));
|
|
ae_vector_clear(y);
|
|
ae_vector_init(&dummy, 0, DT_BOOL, _state, ae_true);
|
|
|
|
ae_assert(n0>0, "RBFGridCalc2V: invalid value for N0 (N0<=0)!", _state);
|
|
ae_assert(n1>0, "RBFGridCalc2V: invalid value for N1 (N1<=0)!", _state);
|
|
ae_assert(x0->cnt>=n0, "RBFGridCalc2V: Length(X0)<N0", _state);
|
|
ae_assert(x1->cnt>=n1, "RBFGridCalc2V: Length(X1)<N1", _state);
|
|
ae_assert(isfinitevector(x0, n0, _state), "RBFGridCalc2V: X0 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x1, n1, _state), "RBFGridCalc2V: X1 contains infinite or NaN values!", _state);
|
|
for(i=0; i<=n0-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x0->ptr.p_double[i],x0->ptr.p_double[i+1]), "RBFGridCalc2V: X0 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n1-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x1->ptr.p_double[i],x1->ptr.p_double[i+1]), "RBFGridCalc2V: X1 is not ordered by ascending", _state);
|
|
}
|
|
rbfgridcalc2vx(s, x0, n0, x1, n1, &dummy, ae_false, y, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at some subset of regular
|
|
grid:
|
|
* grid has N0*N1 points, with Point[I,J] = (X0[I], X1[J])
|
|
* only values at some subset of this grid are required
|
|
Vector-valued RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>2
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
FlagY - array[N0*N1]:
|
|
* Y[I0+I1*N0] corresponds to node (X0[I0],X1[I1])
|
|
* it is a "bitmap" array which contains False for nodes
|
|
which are NOT calculated, and True for nodes which are
|
|
required.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1*N2], where NY is a number
|
|
of "output" vector values (this function supports vector-
|
|
valued RBF models):
|
|
* Y[K+NY*(I0+I1*N0)]=F_k(X0[I0],X1[I1]),
|
|
for K=0...NY-1, I0=0...N0-1, I1=0...N1-1.
|
|
* elements of Y[] which correspond to FlagY[]=True are
|
|
loaded by model values (which may be exactly zero for
|
|
some nodes).
|
|
* elements of Y[] which correspond to FlagY[]=False MAY be
|
|
initialized by zeros OR may be calculated. This function
|
|
processes grid as a hierarchy of nested blocks and
|
|
micro-rows. If just one element of micro-row is required,
|
|
entire micro-row (up to 8 nodes in the current version,
|
|
but no promises) is calculated.
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc2vsubset(rbfmodel* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Boolean */ ae_vector* flagy,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
ae_vector_clear(y);
|
|
|
|
ae_assert(n0>0, "RBFGridCalc2VSubset: invalid value for N0 (N0<=0)!", _state);
|
|
ae_assert(n1>0, "RBFGridCalc2VSubset: invalid value for N1 (N1<=0)!", _state);
|
|
ae_assert(x0->cnt>=n0, "RBFGridCalc2VSubset: Length(X0)<N0", _state);
|
|
ae_assert(x1->cnt>=n1, "RBFGridCalc2VSubset: Length(X1)<N1", _state);
|
|
ae_assert(flagy->cnt>=n0*n1, "RBFGridCalc2VSubset: Length(FlagY)<N0*N1*N2", _state);
|
|
ae_assert(isfinitevector(x0, n0, _state), "RBFGridCalc2VSubset: X0 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x1, n1, _state), "RBFGridCalc2VSubset: X1 contains infinite or NaN values!", _state);
|
|
for(i=0; i<=n0-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x0->ptr.p_double[i],x0->ptr.p_double[i+1]), "RBFGridCalc2VSubset: X0 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n1-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x1->ptr.p_double[i],x1->ptr.p_double[i+1]), "RBFGridCalc2VSubset: X1 is not ordered by ascending", _state);
|
|
}
|
|
rbfgridcalc2vx(s, x0, n0, x1, n1, flagy, ae_true, y, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at the regular grid,
|
|
which has N0*N1*N2 points, with Point[I,J,K] = (X0[I], X1[J], X2[K]).
|
|
Vector-valued RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>3
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
X2 - array of grid nodes, third coordinates, array[N2]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N2 - grid size (number of nodes) in the third dimension
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1*N2], where NY is a number
|
|
of "output" vector values (this function supports vector-
|
|
valued RBF models). Y is out-variable and is reallocated
|
|
by this function.
|
|
Y[K+NY*(I0+I1*N0+I2*N0*N1)]=F_k(X0[I0],X1[I1],X2[I2]), for:
|
|
* K=0...NY-1
|
|
* I0=0...N0-1
|
|
* I1=0...N1-1
|
|
* I2=0...N2-1
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
NOTE: if you need function values on some subset of regular grid, which
|
|
may be described as "several compact and dense islands", you may
|
|
use rbfgridcalc3vsubset().
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc3v(rbfmodel* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_vector dummy;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&dummy, 0, sizeof(dummy));
|
|
ae_vector_clear(y);
|
|
ae_vector_init(&dummy, 0, DT_BOOL, _state, ae_true);
|
|
|
|
ae_assert(n0>0, "RBFGridCalc3V: invalid value for N0 (N0<=0)!", _state);
|
|
ae_assert(n1>0, "RBFGridCalc3V: invalid value for N1 (N1<=0)!", _state);
|
|
ae_assert(n2>0, "RBFGridCalc3V: invalid value for N2 (N2<=0)!", _state);
|
|
ae_assert(x0->cnt>=n0, "RBFGridCalc3V: Length(X0)<N0", _state);
|
|
ae_assert(x1->cnt>=n1, "RBFGridCalc3V: Length(X1)<N1", _state);
|
|
ae_assert(x2->cnt>=n2, "RBFGridCalc3V: Length(X2)<N2", _state);
|
|
ae_assert(isfinitevector(x0, n0, _state), "RBFGridCalc3V: X0 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x1, n1, _state), "RBFGridCalc3V: X1 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x2, n2, _state), "RBFGridCalc3V: X2 contains infinite or NaN values!", _state);
|
|
for(i=0; i<=n0-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x0->ptr.p_double[i],x0->ptr.p_double[i+1]), "RBFGridCalc3V: X0 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n1-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x1->ptr.p_double[i],x1->ptr.p_double[i+1]), "RBFGridCalc3V: X1 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n2-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x2->ptr.p_double[i],x2->ptr.p_double[i+1]), "RBFGridCalc3V: X2 is not ordered by ascending", _state);
|
|
}
|
|
rbfgridcalc3vx(s, x0, n0, x1, n1, x2, n2, &dummy, ae_false, y, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function calculates values of the RBF model at some subset of regular
|
|
grid:
|
|
* grid has N0*N1*N2 points, with Point[I,J,K] = (X0[I], X1[J], X2[K])
|
|
* only values at some subset of this grid are required
|
|
Vector-valued RBF models are supported.
|
|
|
|
This function returns 0.0 when:
|
|
* model is not initialized
|
|
* NX<>3
|
|
|
|
! COMMERCIAL EDITION OF ALGLIB:
|
|
!
|
|
! Commercial Edition of ALGLIB includes following important improvements
|
|
! of this function:
|
|
! * high-performance native backend with same C# interface (C# version)
|
|
! * multithreading support (C++ and C# versions)
|
|
!
|
|
! We recommend you to read 'Working with commercial version' section of
|
|
! ALGLIB Reference Manual in order to find out how to use performance-
|
|
! related features provided by commercial edition of ALGLIB.
|
|
|
|
NOTE: Parallel processing is implemented only for modern (hierarchical)
|
|
RBFs. Legacy version 1 RBFs (created by QNN or RBF-ML) are still
|
|
processed serially.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model, used in read-only mode, can be shared between
|
|
multiple invocations of this function from multiple
|
|
threads.
|
|
|
|
X0 - array of grid nodes, first coordinates, array[N0].
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N0 - grid size (number of nodes) in the first dimension
|
|
|
|
X1 - array of grid nodes, second coordinates, array[N1]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N1 - grid size (number of nodes) in the second dimension
|
|
|
|
X2 - array of grid nodes, third coordinates, array[N2]
|
|
Must be ordered by ascending. Exception is generated
|
|
if the array is not correctly ordered.
|
|
N2 - grid size (number of nodes) in the third dimension
|
|
|
|
FlagY - array[N0*N1*N2]:
|
|
* Y[I0+I1*N0+I2*N0*N1] corresponds to node (X0[I0],X1[I1],X2[I2])
|
|
* it is a "bitmap" array which contains False for nodes
|
|
which are NOT calculated, and True for nodes which are
|
|
required.
|
|
|
|
OUTPUT PARAMETERS:
|
|
Y - function values, array[NY*N0*N1*N2], where NY is a number
|
|
of "output" vector values (this function supports vector-
|
|
valued RBF models):
|
|
* Y[K+NY*(I0+I1*N0+I2*N0*N1)]=F_k(X0[I0],X1[I1],X2[I2]),
|
|
for K=0...NY-1, I0=0...N0-1, I1=0...N1-1, I2=0...N2-1.
|
|
* elements of Y[] which correspond to FlagY[]=True are
|
|
loaded by model values (which may be exactly zero for
|
|
some nodes).
|
|
* elements of Y[] which correspond to FlagY[]=False MAY be
|
|
initialized by zeros OR may be calculated. This function
|
|
processes grid as a hierarchy of nested blocks and
|
|
micro-rows. If just one element of micro-row is required,
|
|
entire micro-row (up to 8 nodes in the current version,
|
|
but no promises) is calculated.
|
|
|
|
NOTE: this function supports weakly ordered grid nodes, i.e. you may have
|
|
X[i]=X[i+1] for some i. It does not provide you any performance
|
|
benefits due to duplication of points, just convenience and
|
|
flexibility.
|
|
|
|
NOTE: this function is re-entrant, i.e. you may use same rbfmodel
|
|
structure in multiple threads calling this function for different
|
|
grids.
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc3vsubset(rbfmodel* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Boolean */ ae_vector* flagy,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_int_t i;
|
|
|
|
ae_vector_clear(y);
|
|
|
|
ae_assert(n0>0, "RBFGridCalc3VSubset: invalid value for N0 (N0<=0)!", _state);
|
|
ae_assert(n1>0, "RBFGridCalc3VSubset: invalid value for N1 (N1<=0)!", _state);
|
|
ae_assert(n2>0, "RBFGridCalc3VSubset: invalid value for N2 (N2<=0)!", _state);
|
|
ae_assert(x0->cnt>=n0, "RBFGridCalc3VSubset: Length(X0)<N0", _state);
|
|
ae_assert(x1->cnt>=n1, "RBFGridCalc3VSubset: Length(X1)<N1", _state);
|
|
ae_assert(x2->cnt>=n2, "RBFGridCalc3VSubset: Length(X2)<N2", _state);
|
|
ae_assert(flagy->cnt>=n0*n1*n2, "RBFGridCalc3VSubset: Length(FlagY)<N0*N1*N2", _state);
|
|
ae_assert(isfinitevector(x0, n0, _state), "RBFGridCalc3VSubset: X0 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x1, n1, _state), "RBFGridCalc3VSubset: X1 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x2, n2, _state), "RBFGridCalc3VSubset: X2 contains infinite or NaN values!", _state);
|
|
for(i=0; i<=n0-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x0->ptr.p_double[i],x0->ptr.p_double[i+1]), "RBFGridCalc3VSubset: X0 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n1-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x1->ptr.p_double[i],x1->ptr.p_double[i+1]), "RBFGridCalc3VSubset: X1 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n2-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x2->ptr.p_double[i],x2->ptr.p_double[i+1]), "RBFGridCalc3VSubset: X2 is not ordered by ascending", _state);
|
|
}
|
|
rbfgridcalc3vx(s, x0, n0, x1, n1, x2, n2, flagy, ae_true, y, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function, depending on SparseY, acts as RBFGridCalc2V (SparseY=False)
|
|
or RBFGridCalc2VSubset (SparseY=True) function. See comments for these
|
|
functions for more information
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc2vx(rbfmodel* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Boolean */ ae_vector* flagy,
|
|
ae_bool sparsey,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
ae_int_t ylen;
|
|
hqrndstate rs;
|
|
ae_vector dummyx2;
|
|
ae_vector dummyx3;
|
|
ae_int_t i;
|
|
ae_int_t j;
|
|
ae_int_t k;
|
|
ae_int_t l;
|
|
ae_vector tx;
|
|
ae_vector ty;
|
|
ae_int_t dstoffs;
|
|
rbfcalcbuffer calcbuf;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&rs, 0, sizeof(rs));
|
|
memset(&dummyx2, 0, sizeof(dummyx2));
|
|
memset(&dummyx3, 0, sizeof(dummyx3));
|
|
memset(&tx, 0, sizeof(tx));
|
|
memset(&ty, 0, sizeof(ty));
|
|
memset(&calcbuf, 0, sizeof(calcbuf));
|
|
_hqrndstate_init(&rs, _state, ae_true);
|
|
ae_vector_init(&dummyx2, 0, DT_REAL, _state, ae_true);
|
|
ae_vector_init(&dummyx3, 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);
|
|
_rbfcalcbuffer_init(&calcbuf, _state, ae_true);
|
|
|
|
ae_assert(n0>0, "RBFGridCalc2VX: invalid value for N0 (N0<=0)!", _state);
|
|
ae_assert(n1>0, "RBFGridCalc2VX: invalid value for N1 (N1<=0)!", _state);
|
|
ae_assert(x0->cnt>=n0, "RBFGridCalc2VX: Length(X0)<N0", _state);
|
|
ae_assert(x1->cnt>=n1, "RBFGridCalc2VX: Length(X1)<N1", _state);
|
|
ae_assert(isfinitevector(x0, n0, _state), "RBFGridCalc2VX: X0 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x1, n1, _state), "RBFGridCalc2VX: X1 contains infinite or NaN values!", _state);
|
|
for(i=0; i<=n0-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x0->ptr.p_double[i],x0->ptr.p_double[i+1]), "RBFGridCalc2VX: X0 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n1-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x1->ptr.p_double[i],x1->ptr.p_double[i+1]), "RBFGridCalc2VX: X1 is not ordered by ascending", _state);
|
|
}
|
|
|
|
/*
|
|
* Prepare local variables
|
|
*/
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
hqrndseed(325, 46345, &rs, _state);
|
|
|
|
/*
|
|
* Prepare output array
|
|
*/
|
|
ylen = ny*n0*n1;
|
|
ae_vector_set_length(y, ylen, _state);
|
|
for(i=0; i<=ylen-1; i++)
|
|
{
|
|
y->ptr.p_double[i] = (double)(0);
|
|
}
|
|
if( s->nx!=2 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process V2 model
|
|
*/
|
|
if( s->modelversion==2 )
|
|
{
|
|
ae_vector_set_length(&dummyx2, 1, _state);
|
|
dummyx2.ptr.p_double[0] = (double)(0);
|
|
ae_vector_set_length(&dummyx3, 1, _state);
|
|
dummyx3.ptr.p_double[0] = (double)(0);
|
|
rbfv2gridcalcvx(&s->model2, x0, n0, x1, n1, &dummyx2, 1, &dummyx3, 1, flagy, sparsey, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Reference code for V1 models
|
|
*/
|
|
if( s->modelversion==1 )
|
|
{
|
|
ae_vector_set_length(&tx, nx, _state);
|
|
rbfcreatecalcbuffer(s, &calcbuf, _state);
|
|
for(i=0; i<=n0-1; i++)
|
|
{
|
|
for(j=0; j<=n1-1; j++)
|
|
{
|
|
k = i+j*n0;
|
|
dstoffs = ny*k;
|
|
if( sparsey&&!flagy->ptr.p_bool[k] )
|
|
{
|
|
for(l=0; l<=ny-1; l++)
|
|
{
|
|
y->ptr.p_double[l+dstoffs] = (double)(0);
|
|
}
|
|
continue;
|
|
}
|
|
tx.ptr.p_double[0] = x0->ptr.p_double[i];
|
|
tx.ptr.p_double[1] = x1->ptr.p_double[j];
|
|
rbftscalcbuf(s, &calcbuf, &tx, &ty, _state);
|
|
for(l=0; l<=ny-1; l++)
|
|
{
|
|
y->ptr.p_double[l+dstoffs] = ty.ptr.p_double[l];
|
|
}
|
|
}
|
|
}
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Unknown model
|
|
*/
|
|
ae_assert(ae_false, "RBFGradCalc3VX: integrity check failed", _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function, depending on SparseY, acts as RBFGridCalc3V (SparseY=False)
|
|
or RBFGridCalc3VSubset (SparseY=True) function. See comments for these
|
|
functions for more information
|
|
|
|
-- ALGLIB --
|
|
Copyright 04.03.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfgridcalc3vx(rbfmodel* s,
|
|
/* Real */ ae_vector* x0,
|
|
ae_int_t n0,
|
|
/* Real */ ae_vector* x1,
|
|
ae_int_t n1,
|
|
/* Real */ ae_vector* x2,
|
|
ae_int_t n2,
|
|
/* Boolean */ ae_vector* flagy,
|
|
ae_bool sparsey,
|
|
/* Real */ ae_vector* y,
|
|
ae_state *_state)
|
|
{
|
|
ae_frame _frame_block;
|
|
ae_int_t i;
|
|
ae_int_t ylen;
|
|
ae_int_t nx;
|
|
ae_int_t ny;
|
|
double rmax;
|
|
ae_vector blocks0;
|
|
ae_vector blocks1;
|
|
ae_vector blocks2;
|
|
ae_int_t blockscnt0;
|
|
ae_int_t blockscnt1;
|
|
ae_int_t blockscnt2;
|
|
double blockwidth;
|
|
double searchradius;
|
|
double avgfuncpernode;
|
|
ae_int_t ntrials;
|
|
ae_int_t maxblocksize;
|
|
gridcalc3v1buf bufseedv1;
|
|
ae_shared_pool bufpool;
|
|
hqrndstate rs;
|
|
ae_vector dummyx3;
|
|
|
|
ae_frame_make(_state, &_frame_block);
|
|
memset(&blocks0, 0, sizeof(blocks0));
|
|
memset(&blocks1, 0, sizeof(blocks1));
|
|
memset(&blocks2, 0, sizeof(blocks2));
|
|
memset(&bufseedv1, 0, sizeof(bufseedv1));
|
|
memset(&bufpool, 0, sizeof(bufpool));
|
|
memset(&rs, 0, sizeof(rs));
|
|
memset(&dummyx3, 0, sizeof(dummyx3));
|
|
ae_vector_init(&blocks0, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&blocks1, 0, DT_INT, _state, ae_true);
|
|
ae_vector_init(&blocks2, 0, DT_INT, _state, ae_true);
|
|
_gridcalc3v1buf_init(&bufseedv1, _state, ae_true);
|
|
ae_shared_pool_init(&bufpool, _state, ae_true);
|
|
_hqrndstate_init(&rs, _state, ae_true);
|
|
ae_vector_init(&dummyx3, 0, DT_REAL, _state, ae_true);
|
|
|
|
ae_assert(n0>0, "RBFGridCalc3V: invalid value for N0 (N0<=0)!", _state);
|
|
ae_assert(n1>0, "RBFGridCalc3V: invalid value for N1 (N1<=0)!", _state);
|
|
ae_assert(n2>0, "RBFGridCalc3V: invalid value for N2 (N2<=0)!", _state);
|
|
ae_assert(x0->cnt>=n0, "RBFGridCalc3V: Length(X0)<N0", _state);
|
|
ae_assert(x1->cnt>=n1, "RBFGridCalc3V: Length(X1)<N1", _state);
|
|
ae_assert(x2->cnt>=n2, "RBFGridCalc3V: Length(X2)<N2", _state);
|
|
ae_assert(isfinitevector(x0, n0, _state), "RBFGridCalc3V: X0 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x1, n1, _state), "RBFGridCalc3V: X1 contains infinite or NaN values!", _state);
|
|
ae_assert(isfinitevector(x2, n2, _state), "RBFGridCalc3V: X2 contains infinite or NaN values!", _state);
|
|
for(i=0; i<=n0-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x0->ptr.p_double[i],x0->ptr.p_double[i+1]), "RBFGridCalc3V: X0 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n1-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x1->ptr.p_double[i],x1->ptr.p_double[i+1]), "RBFGridCalc3V: X1 is not ordered by ascending", _state);
|
|
}
|
|
for(i=0; i<=n2-2; i++)
|
|
{
|
|
ae_assert(ae_fp_less_eq(x2->ptr.p_double[i],x2->ptr.p_double[i+1]), "RBFGridCalc3V: X2 is not ordered by ascending", _state);
|
|
}
|
|
|
|
/*
|
|
* Prepare local variables
|
|
*/
|
|
nx = s->nx;
|
|
ny = s->ny;
|
|
hqrndseed(325, 46345, &rs, _state);
|
|
|
|
/*
|
|
* Prepare output array
|
|
*/
|
|
ylen = ny*n0*n1*n2;
|
|
ae_vector_set_length(y, ylen, _state);
|
|
for(i=0; i<=ylen-1; i++)
|
|
{
|
|
y->ptr.p_double[i] = (double)(0);
|
|
}
|
|
if( s->nx!=3 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process V1 model
|
|
*/
|
|
if( s->modelversion==1 )
|
|
{
|
|
|
|
/*
|
|
* Fast exit for models without centers
|
|
*/
|
|
if( s->model1.nc==0 )
|
|
{
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Prepare seed, create shared pool of temporary buffers
|
|
*/
|
|
ae_vector_set_length(&bufseedv1.cx, nx, _state);
|
|
ae_vector_set_length(&bufseedv1.tx, nx, _state);
|
|
ae_vector_set_length(&bufseedv1.ty, ny, _state);
|
|
ae_vector_set_length(&bufseedv1.expbuf0, n0, _state);
|
|
ae_vector_set_length(&bufseedv1.expbuf1, n1, _state);
|
|
ae_vector_set_length(&bufseedv1.expbuf2, n2, _state);
|
|
kdtreecreaterequestbuffer(&s->model1.tree, &bufseedv1.requestbuf, _state);
|
|
ae_shared_pool_set_seed(&bufpool, &bufseedv1, sizeof(bufseedv1), _gridcalc3v1buf_init, _gridcalc3v1buf_init_copy, _gridcalc3v1buf_destroy, _state);
|
|
|
|
/*
|
|
* Analyze input grid:
|
|
* * analyze average number of basis functions per grid node
|
|
* * partition grid in into blocks
|
|
*/
|
|
rmax = s->model1.rmax;
|
|
blockwidth = 2*rmax;
|
|
maxblocksize = 8;
|
|
searchradius = rmax*rbf_rbffarradius+0.5*ae_sqrt((double)(s->nx), _state)*blockwidth;
|
|
ntrials = 100;
|
|
avgfuncpernode = 0.0;
|
|
for(i=0; i<=ntrials-1; i++)
|
|
{
|
|
bufseedv1.tx.ptr.p_double[0] = x0->ptr.p_double[hqrnduniformi(&rs, n0, _state)];
|
|
bufseedv1.tx.ptr.p_double[1] = x1->ptr.p_double[hqrnduniformi(&rs, n1, _state)];
|
|
bufseedv1.tx.ptr.p_double[2] = x2->ptr.p_double[hqrnduniformi(&rs, n2, _state)];
|
|
avgfuncpernode = avgfuncpernode+(double)kdtreetsqueryrnn(&s->model1.tree, &bufseedv1.requestbuf, &bufseedv1.tx, searchradius, ae_true, _state)/(double)ntrials;
|
|
}
|
|
ae_vector_set_length(&blocks0, n0+1, _state);
|
|
blockscnt0 = 0;
|
|
blocks0.ptr.p_int[0] = 0;
|
|
for(i=1; i<=n0-1; i++)
|
|
{
|
|
if( ae_fp_greater(x0->ptr.p_double[i]-x0->ptr.p_double[blocks0.ptr.p_int[blockscnt0]],blockwidth)||i-blocks0.ptr.p_int[blockscnt0]>=maxblocksize )
|
|
{
|
|
inc(&blockscnt0, _state);
|
|
blocks0.ptr.p_int[blockscnt0] = i;
|
|
}
|
|
}
|
|
inc(&blockscnt0, _state);
|
|
blocks0.ptr.p_int[blockscnt0] = n0;
|
|
ae_vector_set_length(&blocks1, n1+1, _state);
|
|
blockscnt1 = 0;
|
|
blocks1.ptr.p_int[0] = 0;
|
|
for(i=1; i<=n1-1; i++)
|
|
{
|
|
if( ae_fp_greater(x1->ptr.p_double[i]-x1->ptr.p_double[blocks1.ptr.p_int[blockscnt1]],blockwidth)||i-blocks1.ptr.p_int[blockscnt1]>=maxblocksize )
|
|
{
|
|
inc(&blockscnt1, _state);
|
|
blocks1.ptr.p_int[blockscnt1] = i;
|
|
}
|
|
}
|
|
inc(&blockscnt1, _state);
|
|
blocks1.ptr.p_int[blockscnt1] = n1;
|
|
ae_vector_set_length(&blocks2, n2+1, _state);
|
|
blockscnt2 = 0;
|
|
blocks2.ptr.p_int[0] = 0;
|
|
for(i=1; i<=n2-1; i++)
|
|
{
|
|
if( ae_fp_greater(x2->ptr.p_double[i]-x2->ptr.p_double[blocks2.ptr.p_int[blockscnt2]],blockwidth)||i-blocks2.ptr.p_int[blockscnt2]>=maxblocksize )
|
|
{
|
|
inc(&blockscnt2, _state);
|
|
blocks2.ptr.p_int[blockscnt2] = i;
|
|
}
|
|
}
|
|
inc(&blockscnt2, _state);
|
|
blocks2.ptr.p_int[blockscnt2] = n2;
|
|
|
|
/*
|
|
* Perform calculation in multithreaded mode
|
|
*/
|
|
rbfv1gridcalc3vrec(&s->model1, x0, n0, x1, n1, x2, n2, &blocks0, 0, blockscnt0, &blocks1, 0, blockscnt1, &blocks2, 0, blockscnt2, flagy, sparsey, searchradius, avgfuncpernode, &bufpool, y, _state);
|
|
|
|
/*
|
|
* Done
|
|
*/
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Process V2 model
|
|
*/
|
|
if( s->modelversion==2 )
|
|
{
|
|
ae_vector_set_length(&dummyx3, 1, _state);
|
|
dummyx3.ptr.p_double[0] = (double)(0);
|
|
rbfv2gridcalcvx(&s->model2, x0, n0, x1, n1, x2, n2, &dummyx3, 1, flagy, sparsey, y, _state);
|
|
ae_frame_leave(_state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Unknown model
|
|
*/
|
|
ae_assert(ae_false, "RBFGradCalc3VX: integrity check failed", _state);
|
|
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
|
|
For ModelVersion=1:
|
|
* last column - radius, same for all dimensions of
|
|
the function being modelled
|
|
For ModelVersion=2:
|
|
* last NX columns - radii, one per dimension
|
|
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.
|
|
ModelVersion-version of the RBF model:
|
|
* 1 - for models created by QNN and RBF-ML algorithms,
|
|
compatible with ALGLIB 3.10 or earlier.
|
|
* 2 - for models created by HierarchicalRBF, requires
|
|
ALGLIB 3.11 or later
|
|
|
|
-- 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_int_t* modelversion,
|
|
ae_state *_state)
|
|
{
|
|
|
|
*nx = 0;
|
|
*ny = 0;
|
|
ae_matrix_clear(xwr);
|
|
*nc = 0;
|
|
ae_matrix_clear(v);
|
|
*modelversion = 0;
|
|
|
|
if( s->modelversion==1 )
|
|
{
|
|
*modelversion = 1;
|
|
rbfv1unpack(&s->model1, nx, ny, xwr, nc, v, _state);
|
|
return;
|
|
}
|
|
if( s->modelversion==2 )
|
|
{
|
|
*modelversion = 2;
|
|
rbfv2unpack(&s->model2, nx, ny, xwr, nc, v, _state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "RBFUnpack: integrity check failure", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function returns model version.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model
|
|
|
|
RESULT:
|
|
* 1 - for models created by QNN and RBF-ML algorithms,
|
|
compatible with ALGLIB 3.10 or earlier.
|
|
* 2 - for models created by HierarchicalRBF, requires
|
|
ALGLIB 3.11 or later
|
|
|
|
-- ALGLIB --
|
|
Copyright 06.07.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
ae_int_t rbfgetmodelversion(rbfmodel* s, ae_state *_state)
|
|
{
|
|
ae_int_t result;
|
|
|
|
|
|
result = s->modelversion;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is used to peek into hierarchical RBF construction process
|
|
from some other thread and get current progress indicator. It returns
|
|
value in [0,1].
|
|
|
|
IMPORTANT: only HRBFs (hierarchical RBFs) support peeking into progress
|
|
indicator. Legacy RBF-ML and RBF-QNN do not support it. You
|
|
will always get 0 value.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model object
|
|
|
|
RESULT:
|
|
progress value, in [0,1]
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.11.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
double rbfpeekprogress(rbfmodel* s, ae_state *_state)
|
|
{
|
|
double result;
|
|
|
|
|
|
result = (double)s->progress10000/(double)10000;
|
|
return result;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is used to submit a request for termination of the
|
|
hierarchical RBF construction process from some other thread. As result,
|
|
RBF construction is terminated smoothly (with proper deallocation of all
|
|
necessary resources) and resultant model is filled by zeros.
|
|
|
|
A rep.terminationtype=8 will be returned upon receiving such request.
|
|
|
|
IMPORTANT: only HRBFs (hierarchical RBFs) support termination requests.
|
|
Legacy RBF-ML and RBF-QNN do not support it. An attempt to
|
|
terminate their construction will be ignored.
|
|
|
|
IMPORTANT: termination request flag is cleared when the model construction
|
|
starts. Thus, any pre-construction termination requests will be
|
|
silently ignored - only ones submitted AFTER construction has
|
|
actually began will be handled.
|
|
|
|
INPUT PARAMETERS:
|
|
S - RBF model object
|
|
|
|
-- ALGLIB --
|
|
Copyright 17.11.2018 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void rbfrequesttermination(rbfmodel* s, ae_state *_state)
|
|
{
|
|
|
|
|
|
s->terminationrequest = ae_true;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
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);
|
|
|
|
/*
|
|
* V1 model
|
|
*/
|
|
if( model->modelversion==1 )
|
|
{
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_alloc_entry(s);
|
|
rbfv1alloc(s, &model->model1, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* V2 model
|
|
*/
|
|
if( model->modelversion==2 )
|
|
{
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_alloc_entry(s);
|
|
rbfv2alloc(s, &model->model2, _state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "Assertion failed", _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);
|
|
|
|
/*
|
|
* V1 model
|
|
*/
|
|
if( model->modelversion==1 )
|
|
{
|
|
ae_serializer_serialize_int(s, rbf_rbffirstversion, _state);
|
|
rbfv1serialize(s, &model->model1, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* V2 model
|
|
*/
|
|
if( model->modelversion==2 )
|
|
{
|
|
|
|
/*
|
|
* Header
|
|
*/
|
|
ae_serializer_serialize_int(s, rbf_rbfversion2, _state);
|
|
rbfv2serialize(s, &model->model2, _state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "Assertion failed", _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;
|
|
|
|
_rbfmodel_clear(model);
|
|
|
|
rbf_rbfpreparenonserializablefields(model, _state);
|
|
|
|
/*
|
|
* 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||i1==rbf_rbfversion2, "RBFUnserialize: stream header corrupted", _state);
|
|
|
|
/*
|
|
* V1 model
|
|
*/
|
|
if( i1==rbf_rbffirstversion )
|
|
{
|
|
rbfv1unserialize(s, &model->model1, _state);
|
|
model->modelversion = 1;
|
|
model->ny = model->model1.ny;
|
|
model->nx = model->model1.nx;
|
|
rbf_initializev2(model->nx, model->ny, &model->model2, _state);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* V2 model
|
|
*/
|
|
if( i1==rbf_rbfversion2 )
|
|
{
|
|
rbfv2unserialize(s, &model->model2, _state);
|
|
model->modelversion = 2;
|
|
model->ny = model->model2.ny;
|
|
model->nx = model->model2.nx;
|
|
rbf_initializev1(model->nx, model->ny, &model->model1, _state);
|
|
return;
|
|
}
|
|
ae_assert(ae_false, "Assertion failed", _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Initialize empty model
|
|
|
|
-- ALGLIB --
|
|
Copyright 12.05.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbf_rbfpreparenonserializablefields(rbfmodel* s,
|
|
ae_state *_state)
|
|
{
|
|
|
|
|
|
s->n = 0;
|
|
s->hasscale = ae_false;
|
|
s->radvalue = (double)(1);
|
|
s->radzvalue = (double)(5);
|
|
s->nlayers = 0;
|
|
s->lambdav = (double)(0);
|
|
s->aterm = 1;
|
|
s->algorithmtype = 0;
|
|
s->epsort = rbf_eps;
|
|
s->epserr = rbf_eps;
|
|
s->maxits = 0;
|
|
s->nnmaxits = 100;
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Initialize V1 model (skip initialization for NX=1 or NX>3)
|
|
|
|
-- ALGLIB --
|
|
Copyright 12.05.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbf_initializev1(ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv1model* s,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_rbfv1model_clear(s);
|
|
|
|
if( nx==2||nx==3 )
|
|
{
|
|
rbfv1create(nx, ny, s, _state);
|
|
}
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Initialize V2 model
|
|
|
|
-- ALGLIB --
|
|
Copyright 12.05.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbf_initializev2(ae_int_t nx,
|
|
ae_int_t ny,
|
|
rbfv2model* s,
|
|
ae_state *_state)
|
|
{
|
|
|
|
_rbfv2model_clear(s);
|
|
|
|
rbfv2create(nx, ny, s, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
Cleans report fields
|
|
|
|
-- ALGLIB --
|
|
Copyright 16.06.2016 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
static void rbf_clearreportfields(rbfreport* rep, ae_state *_state)
|
|
{
|
|
|
|
|
|
rep->rmserror = _state->v_nan;
|
|
rep->maxerror = _state->v_nan;
|
|
rep->arows = 0;
|
|
rep->acols = 0;
|
|
rep->annz = 0;
|
|
rep->iterationscount = 0;
|
|
rep->nmv = 0;
|
|
rep->terminationtype = 0;
|
|
}
|
|
|
|
|
|
void _rbfcalcbuffer_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfcalcbuffer *p = (rbfcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv1calcbuffer_init(&p->bufv1, _state, make_automatic);
|
|
_rbfv2calcbuffer_init(&p->bufv2, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfcalcbuffer_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfcalcbuffer *dst = (rbfcalcbuffer*)_dst;
|
|
rbfcalcbuffer *src = (rbfcalcbuffer*)_src;
|
|
dst->modelversion = src->modelversion;
|
|
_rbfv1calcbuffer_init_copy(&dst->bufv1, &src->bufv1, _state, make_automatic);
|
|
_rbfv2calcbuffer_init_copy(&dst->bufv2, &src->bufv2, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfcalcbuffer_clear(void* _p)
|
|
{
|
|
rbfcalcbuffer *p = (rbfcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv1calcbuffer_clear(&p->bufv1);
|
|
_rbfv2calcbuffer_clear(&p->bufv2);
|
|
}
|
|
|
|
|
|
void _rbfcalcbuffer_destroy(void* _p)
|
|
{
|
|
rbfcalcbuffer *p = (rbfcalcbuffer*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv1calcbuffer_destroy(&p->bufv1);
|
|
_rbfv2calcbuffer_destroy(&p->bufv2);
|
|
}
|
|
|
|
|
|
void _rbfmodel_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfmodel *p = (rbfmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv1model_init(&p->model1, _state, make_automatic);
|
|
_rbfv2model_init(&p->model2, _state, make_automatic);
|
|
ae_matrix_init(&p->x, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_matrix_init(&p->y, 0, 0, DT_REAL, _state, make_automatic);
|
|
ae_vector_init(&p->s, 0, DT_REAL, _state, make_automatic);
|
|
}
|
|
|
|
|
|
void _rbfmodel_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfmodel *dst = (rbfmodel*)_dst;
|
|
rbfmodel *src = (rbfmodel*)_src;
|
|
dst->nx = src->nx;
|
|
dst->ny = src->ny;
|
|
dst->modelversion = src->modelversion;
|
|
_rbfv1model_init_copy(&dst->model1, &src->model1, _state, make_automatic);
|
|
_rbfv2model_init_copy(&dst->model2, &src->model2, _state, make_automatic);
|
|
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->nnmaxits = src->nnmaxits;
|
|
dst->n = src->n;
|
|
ae_matrix_init_copy(&dst->x, &src->x, _state, make_automatic);
|
|
ae_matrix_init_copy(&dst->y, &src->y, _state, make_automatic);
|
|
dst->hasscale = src->hasscale;
|
|
ae_vector_init_copy(&dst->s, &src->s, _state, make_automatic);
|
|
dst->progress10000 = src->progress10000;
|
|
dst->terminationrequest = src->terminationrequest;
|
|
}
|
|
|
|
|
|
void _rbfmodel_clear(void* _p)
|
|
{
|
|
rbfmodel *p = (rbfmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv1model_clear(&p->model1);
|
|
_rbfv2model_clear(&p->model2);
|
|
ae_matrix_clear(&p->x);
|
|
ae_matrix_clear(&p->y);
|
|
ae_vector_clear(&p->s);
|
|
}
|
|
|
|
|
|
void _rbfmodel_destroy(void* _p)
|
|
{
|
|
rbfmodel *p = (rbfmodel*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
_rbfv1model_destroy(&p->model1);
|
|
_rbfv2model_destroy(&p->model2);
|
|
ae_matrix_destroy(&p->x);
|
|
ae_matrix_destroy(&p->y);
|
|
ae_vector_destroy(&p->s);
|
|
}
|
|
|
|
|
|
void _rbfreport_init(void* _p, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfreport *p = (rbfreport*)_p;
|
|
ae_touch_ptr((void*)p);
|
|
}
|
|
|
|
|
|
void _rbfreport_init_copy(void* _dst, void* _src, ae_state *_state, ae_bool make_automatic)
|
|
{
|
|
rbfreport *dst = (rbfreport*)_dst;
|
|
rbfreport *src = (rbfreport*)_src;
|
|
dst->rmserror = src->rmserror;
|
|
dst->maxerror = src->maxerror;
|
|
dst->arows = src->arows;
|
|
dst->acols = src->acols;
|
|
dst->annz = src->annz;
|
|
dst->iterationscount = src->iterationscount;
|
|
dst->nmv = src->nmv;
|
|
dst->terminationtype = src->terminationtype;
|
|
}
|
|
|
|
|
|
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);
|
|
}
|
|
|
|
|
|
#endif
|
|
#if defined(AE_COMPILE_INTCOMP) || !defined(AE_PARTIAL_BUILD)
|
|
|
|
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspheremc() instead.
|
|
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspheremcc(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
/* Real */ ae_vector* cx,
|
|
double* rhi,
|
|
ae_state *_state)
|
|
{
|
|
double dummy;
|
|
|
|
ae_vector_clear(cx);
|
|
*rhi = 0;
|
|
|
|
nsfitspherex(xy, npoints, nx, 1, 0.0, 0, 0.0, cx, &dummy, rhi, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspheremi() instead.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspheremic(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
/* Real */ ae_vector* cx,
|
|
double* rlo,
|
|
ae_state *_state)
|
|
{
|
|
double dummy;
|
|
|
|
ae_vector_clear(cx);
|
|
*rlo = 0;
|
|
|
|
nsfitspherex(xy, npoints, nx, 2, 0.0, 0, 0.0, cx, rlo, &dummy, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspheremz() instead.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspheremzc(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
/* Real */ ae_vector* cx,
|
|
double* rlo,
|
|
double* rhi,
|
|
ae_state *_state)
|
|
{
|
|
|
|
ae_vector_clear(cx);
|
|
*rlo = 0;
|
|
*rhi = 0;
|
|
|
|
nsfitspherex(xy, npoints, nx, 3, 0.0, 0, 0.0, cx, rlo, rhi, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is left for backward compatibility.
|
|
Use fitspherex() instead.
|
|
|
|
-- ALGLIB --
|
|
Copyright 14.04.2017 by Bochkanov Sergey
|
|
*************************************************************************/
|
|
void nsfitspherex(/* Real */ ae_matrix* xy,
|
|
ae_int_t npoints,
|
|
ae_int_t nx,
|
|
ae_int_t problemtype,
|
|
double epsx,
|
|
ae_int_t aulits,
|
|
double penalty,
|
|
/* Real */ ae_vector* cx,
|
|
double* rlo,
|
|
double* rhi,
|
|
ae_state *_state)
|
|
{
|
|
|
|
ae_vector_clear(cx);
|
|
*rlo = 0;
|
|
*rhi = 0;
|
|
|
|
fitspherex(xy, npoints, nx, problemtype, epsx, aulits, penalty, cx, rlo, rhi, _state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is an obsolete and deprecated version of fitting by
|
|
penalized cubic spline.
|
|
|
|
It was superseded by spline1dfit(), which is an orders of magnitude faster
|
|
and more memory-efficient implementation.
|
|
|
|
Do NOT use this function in the new code!
|
|
|
|
-- 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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&w, 0, sizeof(w));
|
|
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] = (double)(1);
|
|
}
|
|
spline1dfitpenalizedw(x, y, &w, n, m, rho, info, s, rep, _state);
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
This function is an obsolete and deprecated version of fitting by
|
|
penalized cubic spline.
|
|
|
|
It was superseded by spline1dfit(), which is an orders of magnitude faster
|
|
and more memory-efficient implementation.
|
|
|
|
Do NOT use this function in the new code!
|
|
|
|
-- 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);
|
|
memset(&_x, 0, sizeof(_x));
|
|
memset(&_y, 0, sizeof(_y));
|
|
memset(&_w, 0, sizeof(_w));
|
|
memset(&xoriginal, 0, sizeof(xoriginal));
|
|
memset(&yoriginal, 0, sizeof(yoriginal));
|
|
memset(&fmatrix, 0, sizeof(fmatrix));
|
|
memset(&fcolumn, 0, sizeof(fcolumn));
|
|
memset(&y2, 0, sizeof(y2));
|
|
memset(&w2, 0, sizeof(w2));
|
|
memset(&xc, 0, sizeof(xc));
|
|
memset(&yc, 0, sizeof(yc));
|
|
memset(&dc, 0, sizeof(dc));
|
|
memset(&amatrix, 0, sizeof(amatrix));
|
|
memset(&d2matrix, 0, sizeof(d2matrix));
|
|
memset(&bx, 0, sizeof(bx));
|
|
memset(&by, 0, sizeof(by));
|
|
memset(&bd1, 0, sizeof(bd1));
|
|
memset(&bd2, 0, sizeof(bd2));
|
|
memset(&tx, 0, sizeof(tx));
|
|
memset(&ty, 0, sizeof(ty));
|
|
memset(&td, 0, sizeof(td));
|
|
memset(&bs, 0, sizeof(bs));
|
|
memset(&nmatrix, 0, sizeof(nmatrix));
|
|
memset(&rightpart, 0, sizeof(rightpart));
|
|
memset(&cgstate, 0, sizeof(cgstate));
|
|
memset(&c, 0, sizeof(c));
|
|
memset(&tmp0, 0, sizeof(tmp0));
|
|
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((double)(10), _state);
|
|
if( ae_fp_less(rho,-v) )
|
|
{
|
|
rho = -v;
|
|
}
|
|
if( ae_fp_greater(rho,v) )
|
|
{
|
|
rho = v;
|
|
}
|
|
lambdav = ae_pow((double)(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 = (double)(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] = (double)(0);
|
|
}
|
|
by.ptr.p_double[b] = (double)(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 = (double)(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 = (double)(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 = (double)(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] = (double)(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 = (double)(0);
|
|
rep->avgerror = (double)(0);
|
|
rep->avgrelerror = (double)(0);
|
|
rep->maxerror = (double)(0);
|
|
relcnt = (double)(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],(double)(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,(double)(0)) )
|
|
{
|
|
rep->avgrelerror = rep->avgrelerror/relcnt;
|
|
}
|
|
ae_frame_leave(_state);
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
}
|
|
|