library of assembled shared sources

http://lass.cocamware.com

spline_cubic.inl

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
00041  */
00042 
00043 
00044 
00045 #ifndef LASS_GUARDIAN_OF_INCLUSION_NUM_SPLINE_CUBIC_INL
00046 #define LASS_GUARDIAN_OF_INCLUSION_NUM_SPLINE_CUBIC_INL
00047 
00048 #include "num_common.h"
00049 #include "spline_cubic.h"
00050 #include "impl/matrix_solve.h"
00051 #include "../stde/extended_iterator.h"
00052 
00053 namespace lass
00054 {
00055 namespace num
00056 {
00057 
00058 // --- public --------------------------------------------------------------------------------------
00059 
00060 /** construct an empty spline 
00061  */
00062 template <typename S, typename D, typename T>
00063 SplineCubic<S, D, T>::SplineCubic()
00064 {
00065 }
00066 
00067 
00068 
00069 /** construct a spline from a range of control/data pairs.
00070  *
00071  *  Each node consists of a control value and a data value.  This contstructor accepts a single 
00072  *  range [iFirst, iLast) of control/data pairs.  The iterator type should have two fields
00073  *  @c first and @c second that contain respectively the control and data values.  This is
00074  *  choosen so that a std::pair can be used as a representatin of the control/data pair.
00075  *
00076  *  @pre
00077  *  @arg [iFirst, iLast) is a valid range.
00078  *  @arg @c PairInputIterator has a member @c first containing the control value
00079  *  @arg @c PairInputIterator has a member @c second containing the data value
00080  *
00081  *  @par complexity: 
00082  *      O(D * log(N)) with 
00083  *      @arg D = a figure that indicates the complexity of operations on data values.
00084  *               Is most probably linear with the dimension of the data value
00085  *      @arg N = number of nodes
00086  */
00087 template <typename S, typename D, typename T>
00088 template <typename PairInputIterator>
00089 SplineCubic<S, D, T>::SplineCubic(PairInputIterator iFirst, PairInputIterator iLast)
00090 {
00091     while (iFirst != iLast)
00092     {
00093         Node node;
00094         node.x = iFirst->first;
00095         node.d = iFirst->second;
00096         nodes_.push_back(node);
00097         ++iFirst;
00098     }
00099     init();
00100 }
00101 
00102 
00103 
00104 /** construct a spline from seperate ranges.
00105  *
00106  *  Each node consists of a control value and a data value.  This contstructor accepts seperate
00107  *  ranges for control and data values.  The control values are given by the range 
00108  *  [iFirstControl , iLastControl).  Of the range of the data values, only the iterator
00109  *  iFirstData to the the first element is given.
00110  *
00111  *  @pre
00112  *  @arg [iFirstControl, iLastControl) is a valid range.
00113  *  @arg [iFirstData, iFirstData + (iLastControl - iFirstControl)) is a valid range.
00114  *
00115  *  @par complexity: 
00116  *      O(D * log(N)) with 
00117  *      @arg D = a figure that indicates the complexity of operations on data values.
00118  *               Is most probably linear with the dimension of the data value
00119  *      @arg N = number of nodes
00120  */
00121 template <typename S, typename D, typename T>
00122 template <typename ScalarInputIterator, typename DataInputIterator>
00123 SplineCubic<S, D, T>::SplineCubic(ScalarInputIterator iFirstControl, 
00124                                   ScalarInputIterator iLastControl,
00125                                   DataInputIterator iFirstData)
00126 {
00127     while (iFirstControl != iLastControl)
00128     {
00129         Node node;
00130         node.x = *iFirstControl++;
00131         node.d = *iFirstData++;
00132         nodes_.push_back(node);
00133     }
00134     init();
00135 }
00136 
00137 
00138 
00139 /** Get the linear interpolated data value that corresponds with constrol value @a iX.
00140  *
00141  *  @pre this->isEmpty() == false
00142  *
00143  *  @par Complexity: 
00144  *      O(D * log(N)) with 
00145  *      @arg D = a figure that indicates the complexity of operations on data values.
00146  *               Is most probably linear with the dimension of the data value
00147  *      @arg N = number of nodes
00148  */
00149 template <typename S, typename D, typename T>
00150 const typename SplineCubic<S, D, T>::TData
00151 SplineCubic<S, D, T>::operator ()(TScalar iX) const
00152 {
00153     LASS_ASSERT(!isEmpty());
00154 
00155     const TNodeConstIterator n = findNode(iX);
00156     const TScalar s = iX - n->x;
00157     TData result(n->d);
00158     TDataTraits::multiplyAccumulate(result, n->c, s);
00159     TDataTraits::multiplyAccumulate(result, n->b, num::sqr(s));
00160     TDataTraits::multiplyAccumulate(result, n->a, num::cubic(s));
00161     return result;
00162 }
00163 
00164 
00165 
00166 /** Get the first derivative of data value that corresponds with constrol value @a iX.
00167  *
00168  *  @pre this->isEmpty() == false
00169  *
00170  *  @par Complexity: 
00171  *      O(D * log(N)) with 
00172  *      @arg D = a figure that indicates the complexity of operations on data values.
00173  *               Is most probably linear with the dimension of the data value
00174  *      @arg N = number of nodes
00175  */
00176 template <typename S, typename D, typename T>
00177 const typename SplineCubic<S, D, T>::TData
00178 SplineCubic<S, D, T>::derivative(TScalar iX) const
00179 {
00180     LASS_ASSERT(!isEmpty());
00181 
00182     const TNodeConstIterator n = findNode(iX);
00183     const TScalar s = iX - n->x;
00184     TData result(n->c);
00185     TDataTraits::multiplyAccumulate(result, n->b, 2 * s);
00186     TDataTraits::multiplyAccumulate(result, n->a, 3 * num::sqr(s));
00187     return result;
00188 }
00189 
00190 
00191 
00192 /** Get the second derivative of data value that corresponds with constrol value @a iX.
00193  *
00194  *  @pre this->isEmpty() == false
00195  *
00196  *  @par Complexity: 
00197  *      O(D * log(N)) with 
00198  *      @arg D = a figure that indicates the complexity of operations on data values.
00199  *               Is most probably linear with the dimension of the data value
00200  *      @arg N = number of nodes
00201  */
00202 template <typename S, typename D, typename T>
00203 const typename SplineCubic<S, D, T>::TData
00204 SplineCubic<S, D, T>::derivative2(TScalar iX) const
00205 {
00206     LASS_ASSERT(!isEmpty());
00207 
00208     const TNodeConstIterator n = findNode(iX);
00209     const TScalar s = iX - n->x;
00210     TData result(n->b);
00211     TDataTraits::multiplyAccumulate(result, n->a, 6 * s);
00212     return result;
00213 }
00214 
00215 
00216 
00217 /** Get the integrated data value between control points @a iA and @a iB.
00218  *
00219  *  @pre this->isEmpty() == false
00220  *
00221  *  @par Complexity: 
00222  *      O(D * M * log(N)) with 
00223  *      @arg D = a figure that indicates the complexity of operations on data values.
00224  *               Is most probably linear with the dimension of the data value
00225  *      @arg M = number of nodes between @a iA and @a iB.
00226  *      @arg N = total number of nodes in spline
00227  */
00228 template <typename S, typename D, typename T>
00229 const typename SplineCubic<S, D, T>::TData
00230 SplineCubic<S, D, T>::integral(TScalar iBegin, TScalar iEnd) const
00231 {
00232     LASS_ASSERT(!isEmpty());
00233 
00234     TNodeConstIterator first = findNode(iBegin);
00235     TNodeConstIterator last = findNode(iEnd);
00236     if (first == last)
00237     {
00238         const TScalar s1 = iBegin - first->x;
00239         const TScalar s2 = iEnd - first->x;
00240         TData result(first->d);
00241         TDataTraits::scale(result, s2 - s1);
00242         TDataTraits::multiplyAccumulate(result, first->c, (num::sqr(s2) - num::sqr(s1)) / 2);
00243         TDataTraits::multiplyAccumulate(result, first->b, (num::cubic(s2) - num::cubic(s1)) / 3);
00244         TDataTraits::multiplyAccumulate(result, first->a, (num::sqr(num::sqr(s2)) - num::sqr(num::sqr(s1))) / 4);
00245         return result;
00246     }
00247     else
00248     {
00249         TScalar multiplier = 1;
00250         if (iEnd < iBegin)
00251         {
00252             std::swap(iBegin, iEnd);
00253             std::swap(first, last);
00254             multiplier = -1;
00255         }
00256 
00257         TNodeConstIterator next = stde::next(first);
00258 
00259         const TScalar s1 = iBegin - first->x;
00260         const TScalar s2 = next->x - first->x;
00261         TData result(first->d);
00262         TDataTraits::scale(result, s2 - s1);
00263         TDataTraits::multiplyAccumulate(result, first->c, (num::sqr(s2) - num::sqr(s1)) / 2);
00264         TDataTraits::multiplyAccumulate(result, first->b, (num::cubic(s2) - num::cubic(s1)) / 3);
00265         TDataTraits::multiplyAccumulate(result, first->a, (num::sqr(num::sqr(s2)) - num::sqr(num::sqr(s1))) / 4);
00266         first = next++;
00267 
00268         while (first != last)
00269         {
00270             const TScalar s = next->x - first->x;
00271             TDataTraits::multiplyAccumulate(result, first->d, s);
00272             TDataTraits::multiplyAccumulate(result, first->c, num::sqr(s) / 2);
00273             TDataTraits::multiplyAccumulate(result, first->b, num::cubic(s) / 3);
00274             TDataTraits::multiplyAccumulate(result, first->a, num::sqr(num::sqr(s)) / 4);
00275             first = next++;
00276         }
00277 
00278         const TScalar s = iEnd - first->x;
00279         TDataTraits::multiplyAccumulate(result, first->d, s);
00280         TDataTraits::multiplyAccumulate(result, first->c, num::sqr(s) / 2);
00281         TDataTraits::multiplyAccumulate(result, first->b, num::cubic(s) / 3);
00282         TDataTraits::multiplyAccumulate(result, first->a, num::sqr(num::sqr(s)) / 4);
00283 
00284         TDataTraits::scale(result, multiplier);
00285         return result;
00286     }
00287 }
00288 
00289 
00290 
00291 /** return true if the spline contains any nodes at all.
00292  *
00293  *  @par Complexity: 
00294  *      O(1) 
00295  */
00296 template <typename S, typename D, typename T>
00297 const bool SplineCubic<S, D, T>::isEmpty() const
00298 {
00299     return nodes_.empty();
00300 }
00301 
00302 
00303 
00304 /** return the range of control values for which the spline can interpolate.
00305  *  @par complexity: 
00306  *      O(1)
00307  */
00308 template <typename S, typename D, typename T>
00309 const typename SplineCubic<S, D, T>::TControlRange
00310 SplineCubic<S, D, T>::controlRange() const
00311 {
00312     return TControlRange(nodes_.front().x, nodes_.back().x);
00313 }
00314 
00315 
00316 
00317 // --- private -------------------------------------------------------------------------------------
00318 
00319 template <typename S, typename D, typename T>
00320 void SplineCubic<S, D, T>::init()
00321 {
00322     typedef std::vector<TScalar> TVector;
00323 
00324     // are there any nodes at all?  we need at least two!
00325     //
00326     const size_t n = nodes_.size();
00327     if (n < 2)
00328     {
00329         LASS_THROW("A cubic spline needs at least two nodes!  This one only has '"
00330             << static_cast<unsigned>(n) << "'.");
00331     }
00332 
00333     // - check for matching data dimensions
00334     // - check for increasing control components
00335     // - precalculate h_i = x_(i+1) - x_i
00336     //
00337     dataDimension_ = TDataTraits::dimension(nodes_[0].d);
00338     TVector h;
00339     for (size_t i = 1; i < n; ++i)
00340     {
00341         h.push_back(nodes_[i].x - nodes_[i - 1].x);
00342 
00343         if (h.back() <= 0)
00344         {
00345             LASS_THROW("Nodes in cubic spline must have absolutely increasing control components.");
00346         }
00347         if (TDataTraits::dimension(nodes_[i].d) != dataDimension_)
00348         {
00349             LASS_THROW("All data elements in cubic spline must have same dimension.");
00350         }
00351     }
00352 
00353     // --- init some elements ---
00354 
00355     for (size_t i = 0; i < n; ++i)
00356     {
00357         TDataTraits::zero(nodes_[i].b, dataDimension_);
00358     }
00359 
00360     // --- precalculate coefficients. ---
00361 
00362     // find coefficients for tridiagonal matrix
00363     //
00364     const size_t numUnknowns = n - 2;
00365 
00366     //*
00367     TVector ma(numUnknowns);
00368     TVector mb(numUnknowns);
00369     TVector mc(numUnknowns);
00370     TVector temp(numUnknowns);
00371     TVector unknowns(numUnknowns);
00372 
00373     mb[0] = 2 * (h[0] + h[1]);
00374     mc[0] = h[1];
00375     for (size_t i = 1; i < numUnknowns - 1; ++i)
00376     {
00377         ma[i] = h[i];
00378         mb[i] = 2 * (h[i] + h[i + 1]);
00379         mc[i] = h[i + 1];
00380     }
00381     ma[numUnknowns - 1] = h[numUnknowns - 1];
00382     mb[numUnknowns - 1] = 2 * (h[numUnknowns - 1] + h[numUnknowns]);
00383 
00384     for (size_t k = 0; k < dataDimension_; ++k)
00385     {
00386         for (size_t i = 0; i < numUnknowns; ++i)
00387         {
00388             // d_i / h_i - d_(i+1) * (h_i + h_(i+1)) / (h_i * h_(i+1)) + d_(i+2) / h_(i+1)
00389             const TScalar d0 = TDataTraits::get(nodes_[i].d, k);
00390             const TScalar d1 = TDataTraits::get(nodes_[i + 1].d, k);
00391             const TScalar d2 = TDataTraits::get(nodes_[i + 2].d, k);
00392             unknowns[i] = 3 * (d0 / h[i] - d1 * (h[i] + h[i + 1]) / (h[i] * h[i + 1]) + d2 / h[i + 1]);
00393         }
00394         if (!num::impl::solveTridiagonal<TScalar>(\
00395                 ma.begin(), mb.begin(), mc.begin(), unknowns.begin(), temp.begin(), numUnknowns))
00396         {
00397             LASS_THROW("serious logic error, could not solve equation, contact [Bramz]");
00398         }
00399         for (size_t i = 0; i < numUnknowns; ++i)
00400         {
00401             TDataTraits::set(nodes_[i + 1].b, k, unknowns[i]);
00402         }
00403     }
00404 
00405     // find parameters for splines
00406     //
00407     for (size_t i = 0; i < n - 1; ++i)
00408     {
00409         Node& node = nodes_[i];
00410         const Node& nextNode = nodes_[i + 1];
00411 
00412         // a_i = (b_(i+1) - b_i) / (3h_i)
00413         node.a = nextNode.b;
00414         TDataTraits::multiplyAccumulate(node.a, node.b, -1);
00415         TDataTraits::scale(node.a, num::inv(3 * h[i]));
00416 
00417         // c_i = (y_(i+1) - y_i) / h - (M_(i+1) + 2M_i) * h/6
00418         node.c = nextNode.d;
00419         TDataTraits::multiplyAccumulate(node.c, node.d, -1);
00420         TDataTraits::scale(node.c, num::inv(h[i]));
00421         TDataTraits::multiplyAccumulate(node.c, nextNode.b, -h[i] / 3);
00422         TDataTraits::multiplyAccumulate(node.c, node.b, -2 * h[i] / 3);
00423     }
00424 
00425     Node& node = nodes_[n - 1];
00426     const Node& prevNode = nodes_[n - 2];
00427     node.c = prevNode.c;
00428     TDataTraits::multiplyAccumulate(node.c, prevNode.b, 2 * h[n - 2]);
00429     TDataTraits::multiplyAccumulate(node.c, prevNode.a, 3 * num::sqr(h[n - 2]));
00430     node.a = prevNode.a;
00431 
00432 }
00433 
00434 
00435 /** binary search to find node that belongs to iX
00436  *
00437  *  @return
00438  *  @arg the index @a i if @a iX is in the interval [@c nodes_[i].x, @c nodes_[i+1].x)
00439  *  @arg 0 if @a iX is smaller than @c nodes_[0].x</tt>
00440  *  @arg @c nodes_.size()-2 if @a iX is greater than @c nodes_[nodes_.size()-1].x
00441  *
00442  *  @par complexity: 
00443  *      O(log N)
00444  */
00445 template <typename S, typename D, typename T>
00446 const typename SplineCubic<S, D, T>::TNodeConstIterator
00447 SplineCubic<S, D, T>::findNode(TScalar iX) const
00448 {
00449     const typename TNodes::size_type size = nodes_.size();
00450     LASS_ASSERT(nodes_.size() >= 2);
00451 
00452     // early outs
00453     //
00454     if (iX < nodes_[1].x)
00455     {
00456         return nodes_.begin();
00457     }
00458     if (iX >= nodes_[size - 2].x)
00459     {
00460         return stde::prev(nodes_.end(), 2);
00461     }
00462 
00463     // binary search
00464     //
00465     TNodeConstIterator first = nodes_.begin();
00466     TNodeConstIterator last = nodes_.end();
00467     while (stde::next(first) != last)
00468     {
00469         TNodeConstIterator middle = stde::next(first, std::distance(first, last) / 2);
00470         LASS_ASSERT(middle != first && middle != last);
00471 
00472         if (middle->x <= iX)
00473         {
00474             first = middle;
00475         }
00476         else
00477         {
00478             last = middle;
00479         }
00480         LASS_ASSERT(first != last);
00481     }
00482 
00483     LASS_ASSERT(first->x <= iX && last->x > iX);
00484     return first;
00485 }
00486 
00487 
00488 
00489 }
00490 
00491 }
00492 
00493 #endif
00494 
00495 // EOF

Generated on Mon Nov 10 14:21:39 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo