src/qtensor/qtensor.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00024 
00025 #include <qtensor/qtensor.h>
00026 
00027 QTensor &QTensor::copy(const QTensor &tensor)
00028         {
00029         // 1.  Deallocate any memory that MyClass is using internally
00030         // 2.  Allocate some memory to hold the contents of rhs
00031         // 3.  Copy the values from rhs into this instance
00032         indexIds = tensor.indexIds;
00033         data = tensor.data;
00034         
00035         // 4.  Return *this
00036         return *this;
00037         };
00038 
00039 bool QTensor::equals(const QTensor &other) const
00040         {
00042         return true;
00043         }
00044 
00045 QTensor QTensor::tensorProduct(const QTensor &tensor) const
00046         {
00047         // Sean A = *this, B = tensor;
00048         // 0. Si A y B no tienen índices comunes, se devuelve el producto exterior.
00049         // 1. idx = índice más a la izquierda, de la matriz más grande de A, B, o un índice que esté 
00050         //      a la derecha del todo en la más grande.
00051         // 2. Obtenemos A' y B' traspuestas de A y B respectivamente, con el indice idx más a la izquierda
00052         //      (o si ya está en la derecha en alguna de ellas, lo dejamos ahi). De paso movemos los índices
00053         //      variables de ambas matrices a la izquierda.
00054         // 3. Hacemos C = A'.data() * B'.data(), usando dgemm de blas.
00055         // 4. Devolvemos C.contract().
00056 
00057         // Obtenemos los índices de los tensores
00058         const QMap<int, QVector<int> >  indexesQTensor1 = getValence().getIndexesPositions(),
00059                                         indexesQTensor2 = tensor.getValence().getIndexesPositions();
00060 
00061         // Buscamos el índice que se repite en ambos tensores y la posición que ocupa en cada tensor, tal que:
00062         // - Es el de mayor rango o dimensión
00063         // - Es el primer o el último índice del tensor mayor.
00064         // Para ello definimos una función que minimizará dicho índice:
00065         // CostoTranspose(indice, tensor) = ((indice es primero | último en tensor)?0:1) * cardinal(tensor)
00066         // Costo(indice) =      4*cardinal(tensor1)*cardinal(tensor2)/rango(indice)
00067         //                      + 2*(indice no es primero | ultimo en tensor de rango mayor)?1:0
00068         //                      + 1*(indice no es primero | ultimo en tensor de rango menor)?1:0
00069         // Llevamos dicho índice (si no está ya) al primero de los índices, y el resto de índices repetidos
00070         // Delante también
00071         int actualIndexSize = 0, actualQTensor1Position = -1, actualQTensor2Position = -1;
00072 
00073         QMapIterator< int, QVector<int> > idx(indexesQTensor1);
00074         while (idx.hasNext())
00075                 {
00076                 idx.next();
00077                 const int key = idx.key();
00078                 const QVector<int> positionsIndex1 = indexesQTensor1[key], positionsIndex2 = indexesQTensor2[key];
00079 
00080                 if (positionsIndex1.size() > 1)
00081                         std::cerr << "ERROR, índice repetido en primer tensor de producto *." << std::endl;
00082                 else if (positionsIndex2.size() > 1)
00083                         std::cerr << "ERROR, índice repetido en segundo tensor de producto *." << std::endl;
00084 
00085                 else if (positionsIndex1.size() == 1 && positionsIndex2.size() == 1)
00086                         {
00087                         const int position1 = positionsIndex1[0], position2 = positionsIndex2[0];
00088                         const int indexSize = dims[position1];
00089 
00090                         if (indexIds[position1] == tensor.indexIds[position2])
00091                                 std::cerr << "ERROR, índices iguales en ambos tensores en producto *." << std::endl;
00092                         else    // we have a candidate index.
00093                                 if (indexSize > actualIndexSize)
00094                                         {
00095                                         actualIndexSize = indexSize;
00096                                         actualQTensor1Position = position1;
00097                                         actualQTensor2Position = position2;
00098                                         }
00099                         }
00100                 }
00101 
00102         if (actualQTensor1Position == -1 && actualQTensor2Position == -1)
00103                 return this->outerProduct(tensor).contract();
00104         else    if (actualQTensor1Position == -1 || actualQTensor2Position == -1)
00105                 std::cerr << "ERROR, posición para el índice dominante no definida en producto *." << std::endl;
00106 
00107         // Move dominant index to end and start of the tensors
00108         QTensorValence indexList1 = this->getValence(), indexList2 = tensor.getValence();
00109         //std::cerr << "transpose 1: " << actualQTensor1Position << " <-> " << (dims.size()-1) << std::endl;
00110         //std::cerr << "transpose 2: " << actualQTensor2Position << " <-> " << 0 << std::endl;
00111 
00112         //QTensor temp = *this;
00113         //QTensor       t1 = temp.transpose(actualQTensor1Position, dims.size() -1),
00114         //      t2 = tensor.transpose(actualQTensor2Position, 0);
00115 
00116         QTensor t1 = this->transpose(actualQTensor1Position, dims.size()-1), t2 = tensor.transpose(actualQTensor2Position, 0);
00117 
00118         //std::cerr << "temp tensor 1 = " << t1 << std::endl;
00119         //std::cerr << "temp tensor 2 = " << t2 << std::endl;
00120 
00121         indexList1.removeAt(actualQTensor1Position);
00122         indexList2.removeAt(actualQTensor2Position);
00123 
00124         QTensorValence resultIndexList;
00125         resultIndexList << indexList1 << indexList2;
00126         QTensor result(resultIndexList);
00127 
00128         const int k = t2.dims[0], m = t1.getDataSize() / k, n = t2.getDataSize() / k;
00129  
00130         cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
00131                         m, n, k, 1.0,
00132                         t1.getReadData(), k,
00133                         t2.getReadData(), n, 0.0,
00134                         result.getWriteData(), n);
00135         
00136         return result;
00137         };
00138 
00139 QTensor QTensor::innerProduct(const QTensor &tensor) const
00140         {
00142         return tensor;
00143         }
00144 
00145 QTensor QTensor::renameIndexes(const QTensorValence &indexList) const
00146         {
00148         QTensor result = *this;
00149 
00150         for (int i = 0; i < indexList.size(); i++)
00151                 result.indexIds[i] = indexList.at(i).id;
00152 
00153         return result.contract();
00154         }
00155 
00157 
00158 QTensorValence QTensor::getValence()    const
00159         {
00160         QTensorValence result;
00161         for (int i = 0; i < dims.size(); i++)
00162                 result.append(QTensorIndex(dims[i], indexIds[i]));
00163         return result;
00164         }
00165 
00166 QTensor QTensor::slice(const QTensorIndexValues &indexRangeList) const
00167         {
00168         //const QVector <int> dims = getDims();
00169         const int numDims = dims.size();
00170         //const int *indexId = getIndexesIdentifiers();
00171 
00172         QTensorIterator tensorIterator(dims, indexIds, indexRangeList);
00173         QTensorValence idxList;
00174 
00175         for (int i=0; i< numDims; i++)
00176                 if (tensorIterator.numElementsDimension(i) > 1)
00177                         idxList.append(QTensorIndex(tensorIterator.numElementsDimension(i), indexIds[i]));
00178 
00179         QTensor result(idxList);
00180 
00181         double const    *srcData = getReadData();
00182         double          *destData = result.getWriteData();
00183         int             destIndexValue = -tensorIterator.getVectorSize();
00184 
00185         do cblas_dcopy(tensorIterator.getVectorSize(), &(srcData[tensorIterator.getVectorIndex()]), 1, &(destData[destIndexValue += tensorIterator.getVectorSize()]),1);
00186         while (tensorIterator.nextVector());
00187 
00188         return result;
00189         }
00190 
00191 QTensor QTensor::transpose(const QTensorIndex &index1, const QTensorIndex &index2) const
00192         {
00193         int index1Position = -1, index2Position = -1;
00194 
00195         if (index1.id == index2.id)
00196                 return *this;
00197 
00198         for (int n = 0; n< dims.size(); n++)
00199                 {
00200                 if (indexIds[n] == index1.id)
00201                         index1Position = n;
00202                 if (indexIds[n] == index2.id)
00203                         index2Position = n;
00204                 }
00205 
00206         if (index1Position == -1 || index2Position == -1)
00207                 std::cerr << "ERROR, index not found in transpose." << std::endl;
00208 
00209         return transpose(index1Position, index2Position);
00210         }
00211 
00212 QTensor QTensor::transpose(const QTensorValence &indexList) const
00213         {
00214         QMap<int, QVector<int> > indexes = indexList.getIndexesPositions();
00215         for (int i = 0; i < indexIds.size(); i++)
00216                 if (!indexes.contains(ABS(indexIds[i])))
00217                         std::cerr << "ERROR, index " << indexIds[i] << " not found in transpose." << std::endl;
00218 
00219         QVector< int > order(indexIds.size());
00220         for (int i = 0; i < order.size(); i++)
00221                 order[i] = indexes[ABS(indexIds[i])][0];
00222 
00223         return transpose(order);
00224         }
00225 
00226 QVector<int> getSorting(const QVector<int> &values)
00227         {
00228         QVector<int> result(values.size()-1), dupe = values;
00229         for (int i = 0; i < values.size() -1; i++)
00230                 {
00231                 int pivote = i;
00232                 for (int j = i+1; j < values.size(); j++)
00233                         if (dupe[pivote] > dupe[j])
00234                                 pivote = j;
00235                 result[i] = pivote;
00236 
00237                 // Swap value of the 'pivote'
00238                 const int temp = dupe[i];
00239                 dupe[i] = dupe[pivote];
00240                 dupe[pivote] = temp;
00241                 }
00242         return result;
00243         }
00244 
00245 QTensor QTensor::transpose(const int index1Position, const int index2Position) const
00246         {
00247         // If both indexes are the same, return the actual tensor
00248         if (index1Position == index2Position)
00249                 return *this;
00250 
00251         QVector <int> order(dims.size());
00252         for (int i = 0; i < order.size(); i++)
00253                 order[i] = i;
00254 
00255         order[MIN(index1Position, index2Position)] = MAX(index1Position, index2Position);
00256         order[MAX(index1Position, index2Position)] = MIN(index1Position, index2Position);
00257 
00258         return transpose(order);
00259         }
00260 
00261 QTensor QTensor::transpose(const QVector<int> &order) const
00262         {
00263         // TODO: si el orden es el identidad (1, 2, 3, 4, ......, n), devolver copia de este tensor.
00264         QVector<int> sorting = getSorting(order);
00265 
00266         QTensorValence idxList = getValence();
00267 
00268         bool orderedIndexes = true;
00269         int maxIndexPosition = 0;
00270         for (int i = 0; i < sorting.size(); i++)
00271                 if (i != sorting[i])
00272                         {
00273                         idxList.swap(i, maxIndexPosition = sorting[i]);
00274                         orderedIndexes = false;
00275                         }
00276 
00277         // If there are no indexes to move, return the actual tensor
00278         if (orderedIndexes)
00279                 return *this;
00280 
00281         QTensor result(idxList);
00282 
00283         QTensorIndexator ti(result.dims);
00284         const int       vectorSize = ti.getStep(maxIndexPosition);
00285         for (int i = sorting.size()-1; i >=0; i--)
00286                 if (i != sorting[i])
00287                         ti.swapIndexes(i, sorting[i]);
00288 
00289         QTensorIterator tensorIterator(ti, maxIndexPosition);
00290 
00291         const double    *srcData = getReadData();
00292         double          *destData = result.getWriteData();
00293         int             destIndexValue = -vectorSize;
00294 
00295         do cblas_dcopy(vectorSize, &(srcData[destIndexValue += vectorSize]), 1, &(destData[tensorIterator.getVectorIndex()]),1);
00296         while (tensorIterator.nextVector());
00297 
00298         return result;
00299         }
00300 
00301 QTensor QTensor::contract() const
00302         {
00303         const QMap< int, QVector<int> > map = getValence().getIndexesPositions();
00304 
00305         // Create ordering for indexes, and list of indexes for result tensor.
00306         QVector<int> variableIndexesPositions, fixedIndexesPositions;
00307         QVector<int> variableDims;
00308         QTensorValence fixedIndexList;
00309 
00310         bool dupedIndexes = false;
00311         QMapIterator< int, QVector<int> > idx(map);
00312         while (idx.hasNext())
00313                 {
00314                 idx.next();
00315                 QVector<int> v = idx.value();
00316 
00317                 switch(v.size())
00318                         {
00319                         case 1:
00320                                 fixedIndexesPositions.append(v[0]);
00321                                 fixedIndexList.append(QTensorIndex(dims[v[0]],indexIds[v[0]]));
00322                                 break;
00323                         case 2:
00324                                 if (indexIds[v[0]] != -indexIds[v[1]])
00325                                         std::cerr       << "ERROR: two index apperances are not covariant: "
00326                                                         << indexIds[v[0]] << ", " << indexIds[v[1]] << std::endl;
00327                                 variableIndexesPositions.append(v[0]);
00328                                 variableIndexesPositions.append(v[1]);
00329                                 variableDims.append(dims[v[0]]);
00330                                 dupedIndexes = true;
00331                                 break;
00332                         default:
00333                                 std::cerr << "ERROR: more than two index apperances in a tensor" << std::endl;
00334                                 break;
00335                         }
00336                 }
00337 
00338         // If there are no indexes to contract, return the actual tensor
00339         if (!dupedIndexes)
00340                 return *this;
00341 
00342         QVector<int> inverseOrder = variableIndexesPositions + fixedIndexesPositions;
00343         QVector<int> order(inverseOrder.size());
00344         for (int i = 0; i < inverseOrder.size(); i++)
00345                 order[inverseOrder[i]] = i;
00346 
00347         // Transpose original tensor, and create result tensor.
00348         const QTensor Transposed = transpose(order);
00349         QTensor result(fixedIndexList);
00350 
00351         // Create iterator for variable indexes, and indexator for tensor Transposed
00352         QTensorIterator tensorIterator(variableDims);
00353         QTensorIndexator indexator(Transposed.dims);
00354 
00355         const double    *srcData = Transposed.getReadData();
00356         double          *destData = result.getWriteData();
00357         const int       vectorSize = result.getDataSize();
00358 
00359         for (int i = 0; i < vectorSize; i++)
00360                 destData[i] = 0;
00361 
00362         do      {
00363                 for (int i = 0; i < variableDims.size(); i++)
00364                         {
00365                         indexator.setIndex(2*i, tensorIterator.getIndex(i));
00366                         indexator.setIndex(2*i+1, tensorIterator.getIndex(i));
00367                         }
00368 
00369                 cblas_daxpy(vectorSize, 1, &(srcData[indexator.getMatrixIndex()]), 1, destData, 1);
00370                 }
00371         while (tensorIterator.nextVector());
00372 
00373         return result;
00374         }
00375 
00376 QTensor QTensor::outerProduct(const QTensor &tensor) const
00377         {
00378         QTensorValence indexList;
00379 
00380         for (int i = 0; i < getValence().size(); i++)
00381                 indexList.append(getValence()[i]);
00382 
00383         for (int i = 0; i < tensor.getValence().size(); i++)
00384                 indexList.append(tensor.getValence()[i]);
00385 
00386         QTensor result(indexList);
00387 
00388         const double    *src1Data = getReadData();
00389         const double    *src2Data = tensor.getReadData();
00390         double          *destData = result.getWriteData();
00391         const int       vectorSize = tensor.getDataSize();
00392 
00393         for (int i = 0, destIndex = 0; i < getDataSize(); i++, destIndex += vectorSize)
00394                 {
00395                 cblas_dcopy(vectorSize, src2Data, 1, &(destData[destIndex]), 1);
00396                 cblas_dscal(vectorSize, src1Data[i], &(destData[destIndex]), 1);
00397                 }
00398 
00399         return result;
00400         }
00401 
00403 #include <QString>
00404 std::ostream& operator << ( std::ostream &os, const QTensor &tensor )
00405         {
00406         const QVector<int> dims = tensor.dims, indexIds = tensor.indexIds;
00407         const int       numDims = dims.size();
00408 
00409         os << "QTensor <" << ((indexIds[0]<0)?"cov ":"") << ABS(indexIds[0]);
00410 
00411         for (int i=1; i<numDims; i++)
00412                 os << ", " << ((indexIds[i]<0)?"cov ":"") << ABS(indexIds[i]);
00413 
00414         os << "> (" << dims[0];
00415 
00416         for (int i=1; i<numDims; i++)
00417                 os << " x " << dims[i];
00418 
00419         os << ")" << std::endl;
00420 
00421         const double *data = tensor.getReadData();
00422         QTensorIterator tensorIterator(dims);
00423 
00424         do      {
00425                 if (tensorIterator.getIndex(numDims-1) == 0)
00426                         {
00427                         int index = numDims-2;
00428                         while(tensorIterator.getIndex(index) == 0)
00429                                 index--;
00430 
00431                         for (int i = index; i< numDims-2; i++)
00432                                 os << qPrintable(QString(4*(i+1), ' ')) << "[" << std::endl;
00433 
00434                         os << qPrintable(QString(4*(numDims-1), ' ')) << "[ ";
00435                         }
00436 
00437                 os << qPrintable(QString("%1").arg(data[tensorIterator.getVectorIndex()], -8, 'f', 6)) << " ";
00438 
00439                 if (tensorIterator.getIndex(numDims-1) == dims[numDims-1]-1)
00440                         {
00441                         os << "]" << std::endl;
00442                         int index = numDims-2;
00443                         while(tensorIterator.getIndex(index) == dims[index]-1)
00444                                 index--;
00445                         for (int i = numDims-3; i>=index ; i--)
00446                                 os << qPrintable(QString(4*(i+1), ' ')) << "]" << std::endl;
00447                         }
00448                 } while (tensorIterator.nextVector());
00449 
00450         return os;
00451         }
00452 

Generated on Thu Mar 13 19:18:16 2008 for QVision by  doxygen 1.5.3