Object-oriented Scientific Computing Library: Version 0.910
tensor.h
Go to the documentation of this file.
00001 /*
00002   -------------------------------------------------------------------
00003   
00004   Copyright (C) 2006-2012, Andrew W. Steiner
00005   
00006   This file is part of O2scl.
00007   
00008   O2scl is free software; you can redistribute it and/or modify
00009   it under the terms of the GNU General Public License as published by
00010   the Free Software Foundation; either version 3 of the License, or
00011   (at your option) any later version.
00012   
00013   O2scl is distributed in the hope that it will be useful,
00014   but WITHOUT ANY WARRANTY; without even the implied warranty of
00015   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016   GNU General Public License for more details.
00017   
00018   You should have received a copy of the GNU General Public License
00019   along with O2scl. If not, see <http://www.gnu.org/licenses/>.
00020 
00021   -------------------------------------------------------------------
00022 */
00023 #ifndef O2SCL_TENSOR_H
00024 #define O2SCL_TENSOR_H
00025 
00026 /** \file tensor.h
00027     \brief File for definitions of tensors
00028 */
00029 
00030 #include <iostream>
00031 #include <cstdlib>
00032 #include <string>
00033 #include <fstream>
00034 #include <sstream>
00035 
00036 #include <gsl/gsl_matrix.h>
00037 #include <gsl/gsl_ieee_utils.h>
00038 
00039 #include <o2scl/err_hnd.h>
00040 #include <o2scl/uvector_tlate.h>
00041 #include <o2scl/umatrix_tlate.h>
00042 #include <o2scl/smart_interp.h>
00043 
00044 #ifndef DOXYGENP
00045 namespace o2scl {
00046 #endif
00047   
00048   /** \brief Tensor class with arbitrary dimensions
00049 
00050       The elements of a tensor are typically specified as a list of
00051       <tt>size_t</tt> numbers with length equal to the tensor rank.
00052       For a rank-4 tensor named \c t, the element 
00053       <tt>t[1][2][0][3]</tt> can be obtained with something similar to 
00054       \code
00055       size_t ix[4]={1,2,0,3};
00056       double x=t.get(ix);
00057       \endcode
00058 
00059       Empty tensors have zero rank.
00060 
00061       Slices of tensors are subsets obtained from fixing the index of
00062       several dimensions, while letting other dimensions vary. For a
00063       slice with one dimension not fixed, see \ref vector_slice(). For
00064       a slice with two dimensions not fixed, see \ref matrix_slice().
00065 
00066       For I/O with tensors, see \ref hdf_io.h .
00067 
00068       \future Could implement arithmetic operators + and - and some
00069       different products. 
00070 
00071       \future Implement copies to and from vector
00072       and matrices 
00073 
00074       \future Implement tensor contractions, i.e. tensor
00075       = tensor * tensor 
00076 
00077       \future Consider making a template type to replace double,
00078       e.g. <tt>value_t</tt>.
00079 
00080       \future Could be interesting to write an iterator for this class.
00081 
00082       \comment
00083       A tensor is defined to be empty if it's rank is zero. A tensor's
00084       rank should be zero if and only if no memory has been allocated
00085       for it.
00086 
00087       I could have chosen to make set() functions virtual functions
00088       instead of templates. However, many of these functions may be
00089       templates anyway when I generalize this class to hold floats or
00090       ints rather than just doubles. 
00091       \endcomment
00092   */
00093   class tensor {
00094 
00095 #ifndef DOXYGEN_INTERNAL
00096 
00097   protected:
00098 
00099     /// The data
00100     uvector data;
00101 
00102     /// A rank-sized array of the sizes of each dimension
00103     uvector_size_t size;
00104 
00105     /// Rank
00106     size_t rk;
00107 
00108 #endif
00109 
00110   public:
00111     
00112     /// Create an empty tensor with zero rank
00113     tensor() {
00114       rk=0;
00115     }
00116 
00117     /** \brief Create a tensor of rank \c rank with sizes given in \c dim
00118         
00119         The parameter \c dim must be a pointer to an array of sizes
00120         with length \c rank. If the user requests any of the sizes to
00121         be zero, this constructor will call the error handler, create
00122         an empty tensor, and will allocate no memory.
00123     */
00124     template<class size_vec_t> 
00125       tensor(size_t rank, const size_vec_t &dim) {
00126       if (rank==0) {
00127         data=0;
00128         size=0;
00129       } else {
00130         for(size_t i=0;i<rk;i++) {
00131           if (dim[i]==0) {        
00132             rk=0;
00133             O2SCL_ERR2("Requested zero size with non-zero rank in ",
00134                        "tensor::tensor<>(size_t, size_vec_t &)",gsl_einval);
00135           }
00136         }
00137         rk=rank;
00138         size.allocate(rk);
00139         size_t tot=1;
00140         for(size_t i=0;i<rk;i++) {
00141           size[i]=dim[i];
00142           tot*=size[i];
00143         }
00144         data.allocate(tot);
00145       }
00146     }
00147 
00148     ~tensor() {
00149     }
00150 
00151     /// \name Set functions
00152     //@{
00153     /// Set the element indexed by \c index to value \c val
00154     template<class size_vec_t>
00155       void set(const size_vec_t &index, double val) {
00156 #if O2SCL_NO_RANGE_CHECK
00157 #else
00158       if (rk==0) {
00159         O2SCL_ERR("Empty tensor in set().",gsl_einval);
00160         return;
00161       }
00162       if (index[0]>=size[0]) {
00163         O2SCL_ERR("Index greater than size in set().",gsl_eindex);
00164         return;
00165       }
00166 #endif
00167       size_t ix=index[0];
00168       for(size_t i=1;i<rk;i++) {
00169 #if O2SCL_NO_RANGE_CHECK
00170 #else
00171         if (index[i]>=size[i]) {
00172           O2SCL_ERR("Index greater than size in get().",gsl_eindex);
00173           return;
00174         }
00175 #endif
00176         ix*=size[i];
00177         ix+=index[i];
00178       }
00179       data[ix]=val;
00180       return;
00181     }
00182 
00183     /// Set all elements in a tensor to some fixed value
00184     void set_all(double x) {
00185       for(size_t i=0;i<total_size();i++) data[i]=x;
00186       return;
00187     }
00188     //@}
00189 
00190     /// \name Get functions
00191     //@{
00192     /// Get the element indexed by \c index
00193     template<class size_vec_t> double &get(const size_vec_t &index) {
00194 #if O2SCL_NO_RANGE_CHECK
00195 #else
00196       if (rk==0) {
00197         O2SCL_ERR("Empty tensor in get().",gsl_einval);
00198         return data[0];
00199       }
00200       if (index[0]>=size[0]) {
00201         O2SCL_ERR("Index greater than size in get().",gsl_eindex);
00202         return data[0];
00203       }
00204 #endif
00205       size_t ix=index[0];
00206       for(size_t i=1;i<rk;i++) {
00207 #if O2SCL_NO_RANGE_CHECK
00208 #else
00209         if (index[i]>=size[i]) {
00210           O2SCL_ERR("Index greater than size in get().",gsl_eindex);
00211           return data[0];
00212         }
00213 #endif
00214         ix*=size[i];
00215         ix+=index[i];
00216       }
00217       return data[ix];
00218     }
00219 
00220     /// Get a const reference to the element indexed by \c index
00221     template<class size_vec_t> 
00222       double const &get(const size_vec_t &index) const {
00223 #if O2SCL_NO_RANGE_CHECK
00224 #else
00225       if (rk==0) {
00226         O2SCL_ERR("Empty tensor in get().",gsl_einval);
00227         return data[0];
00228       }
00229       if (index[0]>=size[0]) {
00230         O2SCL_ERR("Index greater than size in get().",gsl_eindex);
00231         return data[0];
00232       }
00233 #endif
00234       size_t ix=index[0];
00235       for(size_t i=1;i<rk;i++) {
00236 #if O2SCL_NO_RANGE_CHECK
00237 #else
00238         if (index[i]>=size[i]) {
00239           O2SCL_ERR("Index greater than size in get().",gsl_eindex);
00240           return data[0];
00241         }
00242 #endif
00243         ix*=size[i];
00244         ix+=index[i];
00245       }
00246       return data[ix];
00247     }
00248     //@}
00249     
00250     /// \name Slice functions
00251     //@{
00252     /** \brief Fix all but one index to create a vector
00253 
00254         This fixes all of the indices to the values given in \c index
00255         except for the index number \c ix, and returns the
00256         corresponding vector, whose length is equal to the size of the
00257         tensor in that index. The value <tt>index[ix]</tt> is ignored.
00258 
00259         For example, for a rank 3 tensor allocated with
00260         \code
00261         tensor t;
00262         size_t dim[3]={3,4,5};
00263         t.allocate(3,dim);
00264         \endcode
00265         the following code
00266         \code
00267         size_t index[3]={1,0,3};
00268         ovector_view v=t.vector_slice(1,index);
00269         \endcode
00270         Gives a vector \c v of length 4 which refers to the values
00271         <tt>t(1,0,3)</tt>, <tt>t(1,1,3)</tt>, <tt>t(1,2,3)</tt>, and
00272         <tt>t(1,3,3)</tt>.
00273     */
00274     template<class size_vec_t> 
00275       ovector_array_stride vector_slice(size_t ix, const size_vec_t &index) {
00276       if (ix+1>rk) {
00277         O2SCL_ERR("Specified index to large in tensor::vector_slice()",
00278                   gsl_einval);
00279       }
00280       size_t start;
00281       if (ix==0) start=0;
00282       else start=index[0];
00283       for(size_t i=1;i<rk;i++) {
00284         start*=size[i];
00285         if (i!=ix) start+=index[i];
00286       }
00287       size_t stride=1;
00288       for(size_t i=ix+1;i<rk;i++) stride*=size[i];
00289       return ovector_array_stride(size[ix],&(data[start]),stride);
00290     }
00291 
00292     /** \brief Fix all but two indices to create a matrix
00293 
00294         This fixes all of the indices to the values given in \c index
00295         except for the index number \c ix and the last index, and
00296         returns the corresponding matrix, whose size is equal to the
00297         size of the tensor in the two indices which are not fixed.
00298 
00299         Since the last index is always fixed, the parameter \c ix
00300         is limited to be \f$ \in [0,\mathrm{rank}-2] \f$.
00301     */
00302     template<class size_vec_t> 
00303       omatrix_array matrix_slice(size_t ix, const size_vec_t &index) {
00304       if (ix+1>=rk) {
00305         O2SCL_ERR("Specified index to large in tensor::matrix_slice()",
00306                   gsl_einval);
00307       }
00308       size_t start=0, tot=size[0];
00309       for(size_t i=1;i<rk;i++) {
00310         tot*=size[i];
00311       }
00312       // This O(rank^2) algorithm could possibly 
00313       // be replaced with a O(rank) one in the future
00314       for(size_t i=0;i<rk-1;i++) {
00315         if (i!=ix) {
00316           size_t adj=index[i];
00317           for(size_t j=i+1;j<rk;j++) adj*=size[j];
00318           start+=adj;
00319         }
00320       }
00321       size_t tda=1;
00322       if (ix==rk-1) {
00323         tda=0;
00324       } else {
00325         for(size_t i=ix+1;i<rk;i++) tda*=size[i];
00326       }
00327       return omatrix_array(&(data[start]),tda,size[ix],size[rk-1]);
00328     }
00329     //@}
00330 
00331     /// \name Memory allocation
00332     //@{
00333     /** \brief Allocate space for a tensor of rank \c rank with sizes
00334         given in \c dim
00335         
00336         The parameter \c dim must be a pointer to an array of sizes
00337         with length \c rank. 
00338 
00339         If memory was previously allocated, it will be freed before
00340         the new allocation.
00341 
00342         If the user requests any of the sizes to be zero, this
00343         function will call the error handler and will allocate no
00344         memory. If memory was previously allocated, the tensor is left
00345         unmodified and no deallocation is performed.
00346     */
00347     template<class size_vec_t> 
00348       void allocate(size_t rank, const size_vec_t &dim) {
00349       for(size_t i=0;i<rk;i++) {
00350         if (dim[i]==0) {
00351           O2SCL_ERR2("Requested zero size in tensor::",
00352                      "tensor(size_t, size_t *)",gsl_einval);
00353         }
00354       }
00355       if (rk>0) free();
00356       rk=rank;
00357       size.allocate(rk);
00358       size_t tot=1;
00359       for(size_t i=0;i<rk;i++) {
00360         size[i]=dim[i];
00361         tot*=size[i];
00362       }
00363       data.allocate(tot);
00364       return;
00365     }
00366 
00367     /// Free allocated space (also sets rank to zero)
00368     void free() {
00369       if (rk>0) {
00370         size.free();
00371         data.free();
00372         rk=0;
00373       }
00374       return;
00375     }
00376     //@}
00377 
00378     /// \name Size functions
00379     //@{
00380     /// Return the rank of the tensor
00381     size_t get_rank() const { return rk; }
00382 
00383     /// Returns the size of the ith index
00384     size_t get_size(size_t i) const { 
00385       if (i<rk) {
00386         return size[i]; 
00387       }
00388       O2SCL_ERR_RET("Requested index exceeding rank in get_size()",
00389                     gsl_einval);
00390     }
00391     
00392     /// Return the full array of sizes
00393     const uvector_size_t &get_size_arr() const {
00394       return size;
00395     }
00396 
00397     /// Return the full array of sizes
00398     const uvector &get_data() const {
00399       return data;
00400     }
00401 
00402 #ifndef DOXYGENP
00403     // For HDF I/O. Undocumented. Probably should be replaced by
00404     // friend function or something else?
00405     uvector &get_data_non_const() {
00406       return data;
00407     }
00408 #endif
00409 
00410     /** \brief Returns the size of the tensor (the product of 
00411         the sizes over every index)
00412     */
00413     size_t total_size() const { 
00414       if (rk==0) return 0;
00415       size_t tot=1;
00416       for(size_t i=0;i<rk;i++) tot*=size[i];
00417       return tot;
00418     }
00419     //@}
00420 
00421     /// \name Index manipulation
00422     //@{
00423     /// Pack the indices into a single array index
00424     template<class size_vec_t> 
00425       size_t pack_indices(const size_vec_t &index) {
00426       if (rk==0) {
00427         O2SCL_ERR("Empty tensor in pack_indices().",gsl_einval);
00428         return 0;
00429       }
00430       if (index[0]>=size[0]) {
00431         O2SCL_ERR("Index greater than size in pack_indices().",gsl_eindex);
00432         return 0;
00433       }
00434       size_t ix=index[0];
00435       for(size_t i=1;i<rk;i++) {
00436         if (index[i]>=size[i]) {
00437           O2SCL_ERR("Index greater than size in pack_indices().",gsl_eindex);
00438           return 0;
00439         }
00440         ix*=size[i];
00441         ix+=index[i];
00442       }
00443       return ix;
00444     }
00445     
00446     /// Unpack the single array index into indices
00447     template<class size_vec_t> 
00448       void unpack_indices(size_t ix, size_vec_t &index) {
00449       if (ix>total_size()) {
00450         O2SCL_ERR("Index greater than total size in unpack_indices().",
00451                   gsl_eindex);
00452         return;
00453       }
00454       size_t ix2, sub_size;
00455       for(size_t i=0;i<rk;i++) {
00456         if (i==rk-1) {
00457           index[i]=ix;
00458         } else {
00459           sub_size=1;
00460           for(size_t j=i+1;j<rk;j++) sub_size*=size[j];
00461           index[i]=ix/sub_size;
00462           // (Remember we're doing integer arithmetic here.)
00463           ix-=sub_size*(ix/sub_size);
00464         }
00465       }
00466       return;
00467     }
00468     //@}
00469 
00470   };
00471 
00472   /** \brief Tensor class with arbitrary dimensions with a grid
00473 
00474       This tensor class allows one to assign the indexes to 
00475       numerical scales, so that n-dimensional interpolation can
00476       be performed. To set the grid, use set_grid() and then
00477       interpolation can be done using interpolate().
00478       
00479       By convention, member functions ending in the _val 
00480       suffix return the closest grid-point to some user-specified
00481       values. 
00482 
00483       \comment
00484       I like how hist_new only allows you to set the
00485       grid if it's the same size as the old grid or the tensor
00486       is empty. This same practice could be applied here, to
00487       force the user to clear the tensor before resetting the grid.
00488       (10/24/11 - Actually, maybe this is a bad idea, because
00489       this class is more analogous to ovector, which doesn't
00490       behave this way.)
00491       \endcomment
00492 
00493       \future Only allocate space for grid if it is set
00494       \future Could implement arithmetic operators + and - and some
00495       different products.
00496       \future Consider creating a set_grid() function which
00497       takes grids from an object like hist_grid. Maybe make a 
00498       constructor for a tensor_grid object which just takes 
00499       as input a set of grids?
00500   */
00501   class tensor_grid : public tensor {
00502 
00503 #ifndef DOXYGEN_INTERNAL
00504 
00505   protected:
00506 
00507     /// A rank-sized set of arrays for the grid points
00508     uvector grid;
00509 
00510     /// If true, the grid has been set by the user
00511     bool grid_set;
00512     
00513     /// The interpolation manager
00514     base_interp_mgr<uvector_base> *bim1;
00515 
00516     /// The subvector interpolation manager
00517     base_interp_mgr<uvector_const_subvector> *bim2;
00518 
00519 #endif
00520 
00521   public:
00522     
00523     /// Create an empty tensor with zero rank
00524   tensor_grid() : tensor() {
00525       grid_set=false;
00526       bim1=&dim1;
00527       bim2=&dim2;
00528     }
00529     
00530     /** \brief Create a tensor of rank \c rank with sizes given in \c dim
00531         
00532         The parameter \c dim must be a pointer to an array of sizes
00533         with length \c rank. If the user requests any of the sizes to
00534         be zero, this constructor will call the error handler, create
00535         an empty tensor, and will allocate no memory.
00536     */
00537     template<class size_vec_t> 
00538       tensor_grid(size_t rank, const size_vec_t &dim) : 
00539     tensor(rank,dim) {
00540       grid_set=false;
00541       for(size_t i=0;i<rk;i++) {
00542         if (dim[i]==0) {
00543           rk=0;
00544           O2SCL_ERR("Requested zero size in tensor::tensor(size_t, size_t *)",
00545                     gsl_einval);
00546         }
00547       }
00548     }
00549 
00550     ~tensor_grid() {
00551     }
00552 
00553     /// \name Set functions
00554     //@{
00555     /// Set the element closest to grid point \c grdp to value \c val
00556     template<class vec_t> 
00557       int set_val(const vec_t &grdp, double val) {
00558 
00559       // Find indices
00560       uvector_size_t index(rk);
00561       for(size_t i=0;i<rk;i++) index[i]=lookup_grid(i,grdp[i]);
00562       
00563       // Pack
00564       size_t ix=index[0];
00565       for(size_t i=1;i<rk;i++) {
00566         ix*=size[i];
00567         ix+=index[i];
00568       }
00569 
00570       // Set value
00571       data[ix]=val;
00572 
00573       return 0;
00574     }
00575 
00576     /** \brief Set the element closest to grid point \c grdp to value 
00577         \c val
00578         
00579         The parameters \c closest and \c grdp may be identical,
00580         allowing one to update a vector \c grdp with the
00581         closest grid point.
00582     */
00583     template<class vec_t, class vec2_t> 
00584       int set_val(const vec_t &grdp, double val, vec2_t &closest) {
00585 
00586       // Find indices
00587       uvector_size_t index(rk);
00588       for(size_t i=0;i<rk;i++) {
00589         index[i]=lookup_grid_val(i,grdp[i],closest[i]);
00590       }
00591       
00592       // Pack
00593       size_t ix=index[0];
00594       for(size_t i=1;i<rk;i++) {
00595         ix*=size[i];
00596         ix+=index[i];
00597       }
00598 
00599       // Set value
00600       data[ix]=val;
00601 
00602       return 0;
00603     }
00604     //@}
00605 
00606     /// \name Get functions
00607     //@{
00608     /// Get the element closest to grid point \c grdp 
00609     template<class vec_t> 
00610       double get_val(const vec_t &grdp) {
00611       
00612       // Find indices
00613       uvector_size_t index(rk);
00614       for(size_t i=0;i<rk;i++) index[i]=lookup_grid(i,grdp[i]);
00615 
00616       // Pack
00617       size_t ix=index[0];
00618       for(size_t i=1;i<rk;i++) {
00619         ix*=size[i];
00620         ix+=index[i];
00621       }
00622 
00623       // Set value
00624       return data[ix];
00625     }
00626 
00627     /// Get the element closest to grid point \c grdp to value \c val
00628     template<class vec_t, class vec2_t> 
00629       double get_val(const vec_t &grdp, const vec2_t &closest) {
00630       
00631       // Find indices
00632       uvector_size_t index(rk);
00633       for(size_t i=0;i<rk;i++) {
00634         index[i]=lookup_grid_val(i,grdp[i],closest[i]);
00635       }
00636       
00637       // Pack
00638       size_t ix=index[0];
00639       for(size_t i=1;i<rk;i++) {
00640         ix*=size[i];
00641         ix+=index[i];
00642       }
00643 
00644       // Set value
00645       return data[ix];
00646     }
00647     //@}
00648     
00649     /// \name Memory allocation
00650     //@{
00651     /** \brief Allocate space for a tensor of rank \c rank with sizes
00652         given in \c dim
00653         
00654         The parameter \c dim must be a pointer to an array of sizes
00655         with length \c rank. 
00656 
00657         If memory was previously allocated, it will be freed before
00658         the new allocation and previously specified grid data will 
00659         be lost.
00660 
00661         If the user requests any of the sizes to be zero, this
00662         function will call the error handler and will allocate no
00663         memory. If memory was previously allocated, the tensor is left
00664         unmodified and no deallocation is performed.
00665     */
00666     template<class size_vec_t>
00667       int allocate(size_t rank, const size_vec_t &dim) {
00668       for(size_t i=0;i<rk;i++) {
00669         if (dim[i]==0) {
00670           O2SCL_ERR2_RET("Requested zero size in ",
00671                          "tensor::tensor(size_t,size_vec_t &)",gsl_einval);
00672         }
00673       }
00674       if (rk>0) free();
00675       rk=rank;
00676       size.allocate(rk);
00677       size_t tot=1;
00678       for(size_t i=0;i<rk;i++) {
00679         size[i]=dim[i];
00680         tot*=size[i];
00681       }
00682       data.allocate(tot);
00683       return 0;
00684     }
00685 
00686     /// Free allocated space (also sets rank to zero)
00687     int free() {
00688       if (rk>0) {
00689         data.free();
00690         grid.free();
00691         size.free();
00692         rk=0;
00693       }
00694       return 0;
00695     }
00696     //@}
00697 
00698     /// \name Grid manipulation
00699     //@{
00700     /// Return true if the grid has been set
00701     bool is_grid_set() const { return grid_set; }
00702 
00703     /** \brief Set the grid
00704 
00705         The grid must be specified for all of the dimensions at
00706         once. Denote \f$ (\mathrm{size})_0 \f$ as the size
00707         of the first dimension, \f$ (\mathrm{size})_1 \f$ as the
00708         size of the second dimesion, and so on. Then the 
00709         first \f$ (\mathrm{size})_0 \f$ entries in \c grid must
00710         be the grid for the first dimension, the next 
00711         \f$ (\mathrm{size})_1 \f$ entries must be the grid
00712         for the second dimension, and so on. Thus \c grid
00713         must be a vector of size
00714         \f[
00715         \sum_{i=0}^{\mathrm{rank}} (\mathrm{size})_i
00716         \f]
00717 
00718         Note that the grid is copied so the function argument may
00719         be destroyed by the user after calling set_grid() without
00720         affecting the tensor grid. 
00721 
00722         \future Define a more generic interface for matrix types
00723     */
00724     template<class vec_t>
00725       int set_grid(const vec_t &grid_vec) {
00726       if (rk==0) {
00727         O2SCL_ERR2_RET("Tried to set grid for empty tensor in ",
00728                        "tensor_grid::set_grid().",gsl_einval);
00729       }
00730       size_t ngrid=0;
00731       for(size_t i=0;i<rk;i++) ngrid+=size[i];
00732       grid.allocate(ngrid);
00733       for(size_t i=0;i<ngrid;i++) {
00734         grid[i]=grid_vec[i];
00735       }
00736       grid_set=true;
00737       return 0;
00738     }
00739 
00740     /// Lookup index for grid closest to \c val
00741     size_t lookup_grid(size_t i, double val) {
00742       if (i<rk && grid_set) {
00743         size_t istart=0;
00744         for(size_t j=0;j<i;j++) istart+=size[j];
00745         size_t best=0;
00746         double min=fabs(grid[istart]-val);
00747         for(size_t j=istart;j<istart+size[i];j++) {
00748           if (fabs(grid[j]-val)<min) {
00749             best=j;
00750             min=fabs(grid[j]-val);
00751           }
00752         }
00753         return best;
00754       }
00755       return 0;
00756     }
00757 
00758     /// Lookup jth value on the ith grid
00759     double get_grid(size_t i, size_t j) const {
00760       if (i<rk && grid_set) {
00761         size_t istart=0;
00762         for(size_t k=0;k<i;k++) istart+=size[k];
00763         return grid[istart+j];
00764       }
00765       return 0;
00766     }
00767 
00768     /** \brief Lookup indices for grid closest point to \c vals
00769 
00770         The values in \c vals are not modified by this function.
00771         
00772         \comment
00773         This function must have a different name than 
00774         lookup_grid() because the template types cause
00775         confusion between the two functions.
00776         \endcomment
00777     */
00778     template<class vec_t, class size_vec_t>
00779       int lookup_grid_vec(const vec_t &vals, 
00780                           const size_vec_t &indices) const {
00781       for(size_t k=0;k<rk;k++) {
00782         indices[k]=lookup_grid(k,vals[k]);
00783       }
00784       return 0;
00785     }
00786 
00787     /// Lookup index for grid closest to \c val, returning the grid point
00788     size_t lookup_grid_val(size_t i, double &val, double &val2) {
00789       if (i<rk && grid_set) {
00790         size_t istart=0;
00791         for(size_t j=0;j<i;j++) istart+=size[j];
00792         size_t best=0;
00793         double min=fabs(grid[istart]-val);
00794         val2=grid[istart];
00795         for(size_t j=istart;j<istart+size[i];j++) {
00796           if (fabs(grid[j]-val)<min) {
00797             best=j;
00798             min=fabs(grid[j]-val);
00799             val2=grid[j];
00800           }
00801         }
00802         return best;
00803       }
00804       return 0;
00805     }
00806   
00807     /// \name Interpolation
00808     //@{
00809     def_interp_mgr<uvector_base,linear_interp> dim1;
00810     def_interp_mgr<uvector_const_subvector,linear_interp> dim2;
00811 
00812     /// Set interpolation managers
00813     int set_interp(base_interp_mgr<uvector_base> &bi1,
00814                    base_interp_mgr<uvector_const_subvector> &bi2) {
00815       bim1=&bi1;
00816       bim2=&bi2;
00817       return 0;
00818     }
00819 
00820     /** \brief Interpolate values \c vals into the tensor, 
00821         returning the result
00822       
00823         This is a quick and dirty implementation of n-dimensional
00824         interpolation by recursive application of the 1-dimensional
00825         routine from \ref smart_interp_vec, using the base
00826         interpolation object specified in the template parameter \c
00827         base_interp_t. This will be slow for sufficiently large data
00828         sets.
00829 
00830         \future Maybe make this a template as well?
00831 
00832         \future It should be straightforward to improve the scaling of
00833         this algorithm significantly by creating a "window" of local
00834         points around the point of interest. This could be done easily
00835         by constructing an initial subtensor. However, this should
00836         probably be superceded by a more generic alternative which
00837         avoids explicit use of the 1-d interpolation types.
00838     */
00839     double interpolate(double *vals) {
00840       typedef smart_interp_vec<uvector_base,uvector_const_subvector,
00841         uvector,uvector_alloc> interp_t;
00842       
00843       if (rk==1) {
00844         
00845         interp_t si(size[0],grid,data);
00846         return si.interp(vals[0]);
00847 
00848       } else {
00849         
00850         // Get total number of interpolations at this level
00851         size_t ss=1;
00852         for(size_t i=1;i<rk;i++) ss*=size[i];
00853 
00854         // Create space for y vectors and interpolators
00855         std::vector<uvector> yvec(ss);
00856         std::vector<interp_t *> si(ss);
00857         for(size_t i=0;i<ss;i++) {
00858           yvec[i].allocate(size[0]);
00859         }
00860         
00861         // Create space for interpolation results
00862         tensor_grid tdat;
00863         uvector_size_t_subvector size_new(size,1,rk-1);
00864         tdat.allocate(rk-1,size_new);
00865 
00866         // Set grid for temporary tensor
00867         uvector_subvector grid_new(grid,size[0],grid.size()-size[0]);
00868         tdat.set_grid(grid_new);
00869         
00870         // Create starting coordinate and counter
00871         uvector_size_t co(rk);
00872         for(size_t i=0;i<rk;i++) co[i]=0;
00873         size_t cnt=0;
00874 
00875         // Loop over every interpolation
00876         bool done=false;
00877         while(done==false) {
00878 
00879           // Fill yvector with the appropriate data
00880           for(size_t i=0;i<size[0];i++) {
00881             co[0]=i;
00882             yvec[cnt][i]=get(co);
00883           }
00884           
00885           si[cnt]=new interp_t(*bim1,*bim2,size[0],grid,yvec[cnt]);
00886           
00887           uvector_size_t_subvector co2(co,1,rk-1);
00888           tdat.set(co2,si[cnt]->interp(vals[0]));
00889 
00890           // Go to next interpolation
00891           cnt++;
00892           co[rk-1]++;
00893           // carry if necessary
00894           for(int j=rk-1;j>0;j--) {
00895             if (co[j]>=size[j]) {
00896               co[j]=0;
00897               co[j-1]++;
00898             }
00899           }
00900 
00901           // Test if done
00902           if (cnt==ss) done=true;
00903 
00904           // End of while loop
00905         }
00906       
00907         // Now call the next level of interpolation
00908         double res=tdat.interpolate(vals+1);
00909         
00910         tdat.free();
00911         for(size_t i=0;i<ss;i++) {
00912           delete si[i];
00913         }
00914 
00915         return res;
00916       }
00917     }
00918     //@}
00919 
00920   };
00921 
00922   /** \brief Rank 1 tensor
00923    */
00924   class tensor1 : public tensor {
00925   public:
00926     
00927     /// Create an empty tensor
00928   tensor1() : tensor() {}
00929 
00930     /// Create a rank 1 tensory of size \c sz
00931   tensor1(size_t sz) : tensor() {
00932       uvector_size_t sizex(1);
00933       sizex[0]=sz;
00934       allocate(1,sizex);
00935     }
00936         
00937     /// Get the element indexed by \c ix
00938     double &get(size_t ix) { return tensor::get(&ix); }
00939 
00940     /// Get the element indexed by \c ix
00941     const double &get(size_t ix) const { return tensor::get(&ix); }
00942         
00943     /// Set the element indexed by \c index to value \c val
00944     void set(size_t index, double val) 
00945     { tensor::set(&index,val); }
00946     
00947     /** \brief Set the element indexed by \c index to value \c val
00948 
00949         (We have to explicitly provide this version since the set()
00950         function is overloaded in this child of \ref tensor.)
00951     */
00952     void set(size_t *index, double val) {
00953       tensor::set(index,val);
00954     }
00955 
00956     /// Get an element using array-like indexing
00957     double &operator[](size_t ix) { return this->data[ix]; }
00958     
00959     /// Get an element using operator()
00960     double &operator()(size_t ix) { return this->data[ix]; }
00961   };
00962 
00963   /** \brief Rank 1 tensor with a grid
00964    */
00965   class tensor_grid1 : public tensor_grid {
00966      
00967   public:
00968      
00969     /// Create an empty tensor
00970   tensor_grid1() : tensor_grid() {}
00971       
00972     /// Create a rank 2 tensor of size \c (sz,sz2,sz3)
00973   tensor_grid1(size_t sz) : tensor_grid() {
00974       this->rk=1;
00975       this->size.allocate(1);
00976       this->size[0]=sz;
00977       this->data.allocate(sz);
00978       this->grid_set=false;
00979     }
00980     
00981     ~tensor_grid1() {
00982     }
00983    
00984     /// Get the element indexed by \c (ix1)
00985     double &get(size_t ix1) { 
00986       size_t sz[1]={ix1};
00987       return tensor_grid::get(sz); 
00988     }
00989 
00990     /// Get the element indexed by \c (ix1)
00991     const double &get(size_t ix1) const { 
00992       size_t sz[1]={ix1};
00993       return tensor_grid::get(sz); 
00994     }
00995  
00996     /// Set the element indexed by \c (ix1) to value \c val
00997     void set(size_t ix1, double val) {
00998       size_t sz[1]={ix1};
00999       tensor_grid::set(sz,val); 
01000     }
01001 
01002     /// Interpolate \c x and return the results
01003     double interp(double x) {
01004       return interpolate(&x);
01005     }
01006   };
01007 
01008   /** \brief Rank 2 tensor
01009    */
01010   class tensor2 : public tensor {
01011   public:
01012 
01013     /// Create an empty tensor
01014   tensor2() : tensor() {}
01015 
01016     /// Create a rank 2 tensor of size \c (sz,sz2)
01017   tensor2(size_t sz, size_t sz2) : tensor() {
01018       this->rk=2;
01019       this->size.allocate(2);
01020       this->size[0]=sz;
01021       this->size[1]=sz2;
01022       size_t tot=sz*sz2;
01023       this->data.allocate(tot);
01024     }
01025         
01026     /// Get the element indexed by \c (ix1,ix2)
01027     double &get(size_t ix1, size_t ix2) { 
01028       size_t sz[2]={ix1,ix2};
01029       return tensor::get(sz); 
01030     }
01031 
01032     /// Get the element indexed by \c (ix1,ix2)
01033     const double &get(size_t ix1, size_t ix2) const { 
01034       size_t sz[2]={ix1,ix2};
01035       return tensor::get(sz); 
01036     }
01037 
01038     /// Set the element indexed by \c (ix1,ix2) to value \c val
01039     void set(size_t ix1, size_t ix2, double val) {
01040       size_t sz[2]={ix1,ix2};
01041       tensor::set(sz,val); 
01042       return;
01043     }
01044 
01045     /** \brief Set the element indexed by \c index to value \c val
01046 
01047         (We have to explicitly provide this version since the set()
01048         function is overloaded in this child of \ref tensor.)
01049     */
01050     void set(size_t *index, double val) {
01051       tensor::set(index,val);
01052       return;
01053     }
01054 
01055     /// Get the element indexed by \c (ix1,ix2)
01056     double &operator()(size_t ix, size_t iy) 
01057     { return this->data[ix*this->size[1]+iy]; }
01058   };
01059   
01060   /** \brief Rank 2 tensor with a grid
01061    */
01062   class tensor_grid2 : public tensor_grid {
01063      
01064   public:
01065      
01066     /// Create an empty tensor
01067   tensor_grid2() : tensor_grid() {}
01068 
01069     /// Create a rank 2 tensor of size \c (sz,sz2)
01070   tensor_grid2(size_t sz, size_t sz2) : tensor_grid() {
01071       this->rk=2;
01072       this->size.allocate(2);
01073       this->size[0]=sz;
01074       this->size[1]=sz2;
01075       size_t tot=sz*sz2;
01076       this->data.allocate(tot);
01077       this->grid_set=false;
01078     }
01079    
01080     ~tensor_grid2() {
01081     }
01082    
01083     /// Get the element indexed by \c (ix1,ix2)
01084     double &get(size_t ix1, size_t ix2) { 
01085       size_t sz[2]={ix1,ix2};
01086       return tensor_grid::get(sz); 
01087     }
01088 
01089     /// Get the element indexed by \c (ix1,ix2)
01090     const double &get(size_t ix1, size_t ix2) const { 
01091       size_t sz[2]={ix1,ix2};
01092       return tensor_grid::get(sz); 
01093     }
01094  
01095     /// Set the element indexed by \c (ix1,ix2) to value \c val
01096     void set(size_t ix1, size_t ix2, double val) {
01097       size_t sz[2]={ix1,ix2};
01098       tensor_grid::set(sz,val); 
01099       return;
01100     }
01101 
01102     /// Interpolate \c (x,y) and return the results
01103     double interp(double x, double y) {
01104       double arr[2]={x,y};
01105       return interpolate(arr);
01106     }
01107   };
01108   
01109   /** \brief Rank 3 tensor
01110    */
01111   class tensor3 : public tensor {
01112   public:
01113 
01114     /// Create an empty tensor
01115   tensor3() : tensor() {}
01116 
01117     /// Create a rank 3 tensor of size \c (sz,sz2,sz3)
01118   tensor3(size_t sz, size_t sz2, size_t sz3) : tensor() {
01119       this->rk=3;
01120       this->size.allocate(3);
01121       this->size[0]=sz;
01122       this->size[1]=sz2;
01123       this->size[2]=sz3;
01124       size_t tot=sz*sz2*sz3;
01125       this->data.allocate(tot);
01126     }
01127         
01128     /// Get the element indexed by \c (ix1,ix2,ix3)
01129     double &get(size_t ix1, size_t ix2, size_t ix3) { 
01130       size_t sz[3]={ix1,ix2,ix3};
01131       return tensor::get(sz); 
01132     }
01133 
01134     /// Get the element indexed by \c (ix1,ix2,ix3)
01135     const double &get(size_t ix1, size_t ix2, size_t ix3) const { 
01136       size_t sz[3]={ix1,ix2,ix3};
01137       return tensor::get(sz); 
01138     }
01139 
01140     /// Set the element indexed by \c (ix1,ix2,ix3) to value \c val
01141     void set(size_t ix1, size_t ix2, size_t ix3, double val) {
01142       size_t sz[3]={ix1,ix2, ix3};
01143       tensor::set(sz,val); 
01144       return;
01145     }
01146 
01147     /** \brief Set the element indexed by \c index to value \c val
01148 
01149         (We have to explicitly provide this version since the set()
01150         function is overloaded in this child of \ref tensor.)
01151     */
01152     void set(size_t *index, double val) {
01153       tensor::set(index,val);
01154       return;
01155     }
01156   };
01157   
01158   /** \brief Rank 3 tensor with a grid
01159    */
01160   class tensor_grid3 : public tensor_grid {
01161      
01162   public:
01163      
01164     /// Create an empty tensor
01165   tensor_grid3() : tensor_grid () {}
01166 
01167     /// Create a rank 3 tensor of size \c (sz,sz2,sz3)
01168   tensor_grid3(size_t sz, size_t sz2, size_t sz3) : tensor_grid () {
01169       this->rk=3;
01170       this->size.allocate(3);
01171       this->size[0]=sz;
01172       this->size[1]=sz2;
01173       this->size[2]=sz3;
01174       size_t tot=sz*sz2*sz3;
01175       this->data.allocate(tot);
01176       this->grid_set=false;
01177     }
01178    
01179     ~tensor_grid3() {
01180     }
01181    
01182     /// Get the element indexed by \c (ix1,ix2,ix3)
01183     double &get(size_t ix1, size_t ix2, size_t ix3) { 
01184       size_t sz[3]={ix1,ix2,ix3};
01185       return tensor_grid::get(sz); 
01186     }
01187  
01188     /// Get the element indexed by \c (ix1,ix2,ix3)
01189     const double &get(size_t ix1, size_t ix2, size_t ix3) const { 
01190       size_t sz[3]={ix1,ix2,ix3};
01191       return tensor_grid::get(sz); 
01192     }
01193  
01194     /// Set the element indexed by \c (ix1,ix2,ix3) to value \c val
01195     void set(size_t ix1, size_t ix2, size_t ix3, double val) {
01196       size_t sz[3]={ix1,ix2, ix3};
01197       tensor_grid::set(sz,val); 
01198       return;
01199     }
01200 
01201     /// Interpolate \c (x,y,z) and return the results
01202     double interp(double x, double y, double z) {
01203       double arr[3]={x,y,z};
01204       return interpolate(arr);
01205     }
01206   };
01207   
01208   /** \brief Rank 4 tensor
01209    */
01210   class tensor4 : public tensor {
01211 
01212   public:
01213 
01214     /// Create an empty tensor
01215   tensor4() : tensor() {}
01216 
01217     /// Create a rank 4 tensor of size \c (sz,sz2,sz3,sz4)
01218   tensor4(size_t sz, size_t sz2, size_t sz3, size_t sz4) : 
01219     tensor() {
01220       this->rk=4;
01221       this->size.allocate(4);
01222       this->size[0]=sz;
01223       this->size[1]=sz2;
01224       this->size[2]=sz3;
01225       this->size[3]=sz4;
01226       size_t tot=sz*sz2*sz3*sz4;
01227       this->data.allocate(tot);
01228     }
01229         
01230     /// Get the element indexed by \c (ix1,ix2,ix3,ix4)
01231     double &get(size_t ix1, size_t ix2, size_t ix3, size_t ix4) { 
01232       size_t sz[4]={ix1,ix2,ix3,ix4};
01233       return tensor::get(sz); 
01234     }
01235 
01236     /// Get the element indexed by \c (ix1,ix2,ix3,ix4)
01237     const double &get(size_t ix1, size_t ix2, size_t ix3, 
01238                       size_t ix4) const { 
01239       size_t sz[4]={ix1,ix2,ix3,ix4};
01240       return tensor::get(sz); 
01241     }
01242 
01243     /// Set the element indexed by \c (ix1,ix2,ix3,ix4) to value \c val
01244     void set(size_t ix1, size_t ix2, size_t ix3, size_t ix4, 
01245             double val) {
01246       size_t sz[4]={ix1,ix2,ix3,ix4};
01247       tensor::set(sz,val); 
01248       return;
01249     }
01250 
01251     /** \brief Set the element indexed by \c index to value \c val
01252 
01253         (We have to explicitly provide this version since the set()
01254         function is overloaded in this child of \ref tensor.)
01255     */
01256     void set(size_t *index, double val) {
01257       tensor::set(index,val);
01258       return;
01259     }
01260   };
01261 
01262   /** \brief Rank 4 tensor with a grid
01263    */
01264   class tensor_grid4 : public tensor_grid {
01265      
01266   public:
01267      
01268     /// Create an empty tensor
01269   tensor_grid4() : tensor_grid () {}
01270 
01271     /// Create a rank 4 tensor of size \c (sz,sz2,sz3,sz4)
01272   tensor_grid4(size_t sz, size_t sz2, size_t sz3,
01273                size_t sz4) : tensor_grid () {
01274       this->rk=4;
01275       this->size.allocate(4);
01276       this->size[0]=sz;
01277       this->size[1]=sz2;
01278       this->size[2]=sz3;
01279       this->size[3]=sz4;
01280       size_t tot=sz*sz2*sz3*sz4;
01281       this->data.allocate(tot);
01282       this->grid_set=false;
01283     }
01284    
01285     ~tensor_grid4() {
01286     }
01287    
01288     /// Get the element indexed by \c (ix1,ix2,ix3,ix4)
01289     double &get(size_t ix1, size_t ix2, size_t ix3, size_t ix4) { 
01290       size_t sz[4]={ix1,ix2,ix3,ix4};
01291       return tensor_grid::get(sz); 
01292     }
01293     
01294     /// Get the element indexed by \c (ix1,ix2,ix3,ix4)
01295     const double &get(size_t ix1, size_t ix2, size_t ix3,
01296                       size_t ix4) const { 
01297       size_t sz[4]={ix1,ix2,ix3,ix4};
01298       return tensor_grid::get(sz); 
01299     }
01300     
01301     /// Set the element indexed by \c (ix1,ix2,ix3,ix4) to value \c val
01302     void set(size_t ix1, size_t ix2, size_t ix3, size_t ix4,
01303             double val) {
01304       size_t sz[4]={ix1,ix2,ix3,ix4};
01305       tensor_grid::set(sz,val); 
01306       return;
01307     }
01308 
01309     /// Interpolate \c (x,y,z,a) and return the results
01310     double interp(double x, double y, double z, double a) {
01311       double arr[4]={x,y,z,a};
01312       return interpolate(arr);
01313     }
01314   };
01315   
01316 #ifndef DOXYGENP
01317 }
01318 #endif
01319 
01320 #endif
01321 
01322 
01323 
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Documentation generated with Doxygen. Provided under the GNU Free Documentation License (see License Information).

Get Object-oriented Scientific Computing
Lib at SourceForge.net. Fast, secure and Free Open Source software
downloads.