File indexing completed on 2024-05-12 03:41:58

0001 /*************************************************************************************
0002  *  Copyright (C) 2014 by Percy Camilo T. Aucahuasi <percy.camilo.ta@gmail.com>      *
0003  *                                                                                   *
0004  *  This program is free software; you can redistribute it and/or                    *
0005  *  modify it under the terms of the GNU General Public License                      *
0006  *  as published by the Free Software Foundation; either version 2                   *
0007  *  of the License, or (at your option) any later version.                           *
0008  *                                                                                   *
0009  *  This program is distributed in the hope that it will be useful,                  *
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of                   *
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                    *
0012  *  GNU General Public License for more details.                                     *
0013  *                                                                                   *
0014  *  You should have received a copy of the GNU General Public License                *
0015  *  along with this program; if not, write to the Free Software                      *
0016  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA   *
0017  *************************************************************************************/
0018 
0019 #include "blockmatrixcommands.h"
0020 
0021 #include <QCoreApplication>
0022 
0023 #include "analitzautils.h"
0024 #include "expression.h"
0025 #include "value.h"
0026 #include "matrix.h"
0027 
0028 using Analitza::Expression;
0029 using Analitza::ExpressionType;
0030 
0031 const QString BlockMatrixCommand::id = QStringLiteral("blockmatrix");
0032 const ExpressionType BlockMatrixCommand::type = ExpressionType(ExpressionType::Lambda)
0033 .addParameter(ExpressionType(ExpressionType::Any, ExpressionType(ExpressionType::Vector, 
0034                                                                  ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1), -1)))
0035 .addParameter(ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1));
0036 
0037 Expression BlockMatrixCommand::operator()(const QList< Analitza::Expression >& args)
0038 {
0039     Expression ret;
0040     
0041     const int nargs = args.size();
0042     
0043     if (nargs == 0) {
0044         ret.addError(QCoreApplication::tr("Invalid parameter count for '%1'").arg(BlockMatrixCommand::id));
0045             
0046         return ret;
0047     }
0048     
0049     const Analitza::Object::ObjectType firstArgType = args.first().tree()->type();
0050     
0051     if (firstArgType == Analitza::Object::vector || firstArgType == Analitza::Object::matrixrow) {
0052         const bool isVector = (firstArgType == Analitza::Object::vector);
0053         const Analitza::Vector *firstVector = static_cast<const Analitza::Vector*>(args.first().tree());
0054         
0055         if (firstVector->size() > 0) { // we will check this for all vectors later
0056             const int firstVectorSize = firstVector->size();
0057             const Analitza::Object::ObjectType firstVectorElementType = firstVector->at(0)->type();
0058             
0059             if (firstVectorElementType == Analitza::Object::matrix) {
0060                 const Analitza::Matrix *firstBlock = static_cast<const Analitza::Matrix*>(firstVector->at(0));
0061                 
0062                 bool isCorrect = true; // this flag tells if is ok to build the block matrix
0063                 int nrows = 0;
0064                 int ncols = 0;
0065                 std::vector<int> blockpattern(firstVectorSize, 0); // if vectors(matrixrow) this tells the row(column) pattern
0066                 
0067                 const int blocklength = isVector? firstBlock->columnCount() : firstBlock->rowCount();
0068                 
0069                 // we need to know the pattern first, this run only on first arg (first vector) 
0070                 for (int blockIndex = 0; blockIndex < firstVectorSize && isCorrect; ++blockIndex) {
0071                     if (firstVector->at(blockIndex)->type() == Analitza::Object::matrix) {
0072                         const Analitza::Matrix* block = static_cast<const Analitza::Matrix*>(firstVector->at(blockIndex));
0073                         
0074                         if (block->rowCount() > 0 && block->columnCount() > 0) {
0075                             const int currentlength = isVector? block->columnCount() : block->rowCount();
0076                             
0077                             if (currentlength == blocklength) {
0078                                 blockpattern[blockIndex] = isVector? block->rowCount() : block->columnCount();
0079                                 
0080                                 if (isVector)
0081                                     nrows += blockpattern[blockIndex];
0082                                 else
0083                                     ncols += blockpattern[blockIndex];
0084                             } else {
0085                                 isCorrect = false;
0086                                 ret.addError(QCoreApplication::tr("Blocks must have consistent size between each and other"));
0087                             }
0088                         } else {
0089                             isCorrect = false;
0090                             ret.addError(QCoreApplication::tr("Do not want empty blocks"));
0091                         }
0092                     } else {
0093                         ret.addError(QCoreApplication::tr("Blocks must be matrices"));
0094                         isCorrect = false;
0095                     }
0096                 }
0097                 
0098                 // check if all args are ok to build a block matrix
0099                 for (int argIndex = 0; argIndex < nargs && isCorrect; ++argIndex) {
0100                     const Analitza::Object::ObjectType currentArgType = args.at(argIndex).tree()->type();
0101                     const Analitza::Vector *vector = static_cast<const Analitza::Vector*>(args.at(argIndex).tree());
0102                     
0103                     if (currentArgType == firstArgType) {
0104                         if (vector->size() > 0) {
0105                             if (vector->size() == firstVectorSize) {
0106                                 const Analitza::Matrix *currentFirstBlock = static_cast<const Analitza::Matrix*>(vector->at(0));
0107                                 const int blocklength = isVector? currentFirstBlock->columnCount() : currentFirstBlock->rowCount();
0108                                 
0109                                 for (int blockIndex = 0; blockIndex < firstVectorSize && isCorrect; ++blockIndex) {
0110                                     if (vector->at(blockIndex)->type() == Analitza::Object::matrix) {
0111                                         const Analitza::Matrix* block = static_cast<const Analitza::Matrix*>(vector->at(blockIndex));
0112                                         
0113                                         if (block->rowCount() > 0 && block->columnCount() > 0) {
0114                                             const int currentlength = isVector? block->columnCount() : block->rowCount();
0115                                             const int currentpattern = isVector? block->rowCount() : block->columnCount();
0116                                             
0117                                             if (currentlength != blocklength) {
0118                                                 isCorrect = false;
0119                                                 ret.addError(QCoreApplication::tr("Blocks must have consistent size between each and other"));
0120                                             } else if (blockpattern[blockIndex] != currentpattern) {
0121                                                 isCorrect = false;
0122                                                 ret.addError(QCoreApplication::tr("Blocks must have consistent size between each and other"));
0123                                             }
0124                                         } else {
0125                                             isCorrect = false;
0126                                             ret.addError(QCoreApplication::tr("Do not want empty blocks"));
0127                                         }
0128                                     } else {
0129                                         isCorrect = false;
0130                                         ret.addError(QCoreApplication::tr("Blocks must be matrices"));
0131                                     }
0132                                 }
0133                                 
0134                                 if (isCorrect) {
0135                                     if (isVector)
0136                                         ncols += blocklength;
0137                                     else
0138                                         nrows += blocklength;
0139                                 }
0140                             } else {
0141                                 isCorrect = false;
0142                                 ret.addError(QCoreApplication::tr("Number of blocks must be consistent"));
0143                             }
0144                         } else {
0145                             ret.addError(QCoreApplication::tr("Do not want empty vectors/matrixrow elements"));
0146                             isCorrect = false;
0147                         }
0148                     } else {
0149                         isCorrect = false;
0150                         ret.addError(QCoreApplication::tr("Matrix constructor needs vectors or matrixrow elements"));
0151                     }
0152                 }
0153                 
0154                 if (isCorrect) {
0155                     Analitza::Matrix *matrix = new Analitza::Matrix();
0156                     
0157                     QVector< QVector< const Analitza::Object* > > objmatrix(nrows, QVector< const Analitza::Object* >(ncols, nullptr));
0158                     
0159                     int nrowsoffset = isVector? nrows : 0;
0160                     int ncolsoffset = isVector? 0 : ncols;
0161                     
0162                     for (int argIndex = 0; argIndex < nargs && isCorrect; ++argIndex) {
0163                         const Analitza::Vector *vector = static_cast<const Analitza::Vector*>(args.at(argIndex).tree());
0164                         
0165                         int blockpattern = 0;
0166                         
0167                         if (isVector)
0168                             nrowsoffset = 0;
0169                         else
0170                             ncolsoffset = 0;
0171                         
0172                         for (int blockIndex = 0; blockIndex < firstVectorSize && isCorrect; ++blockIndex) {
0173                             const Analitza::Matrix* block = static_cast<const Analitza::Matrix*>(vector->at(blockIndex));
0174                             const int m = block->rowCount();
0175                             const int n = block->columnCount();
0176                             
0177                             blockpattern = isVector? n : m;
0178                             
0179                             for (int i = 0; i < m; ++i)
0180                                 for (int j = 0; j < n; ++j)
0181                                     objmatrix[i+nrowsoffset][j+ncolsoffset] = block->at(i,j);
0182                             
0183                             if (isVector)
0184                                 nrowsoffset += m;
0185                             else if (blockIndex == 0) // el patron de cols se define en el primer matrixrow
0186                                 ncolsoffset += n;
0187                         }
0188                         
0189                         if (!isVector)
0190                             nrowsoffset += blockpattern;
0191                         else if (argIndex == 0)
0192                             ncolsoffset += blockpattern;
0193                     }
0194                     
0195                     for (int i = 0; i < nrows; ++i) {
0196                         Analitza::MatrixRow *row = new Analitza::MatrixRow(ncols);
0197                         
0198                         for (int j = 0; j < ncols; ++j)
0199                             row->appendBranch(objmatrix[i][j]->copy());
0200                         
0201                         matrix->appendBranch(row);
0202                     }
0203                     
0204                     ret.setTree(matrix);
0205                 
0206                     return ret;
0207                 }
0208             } else
0209                 ret.addError(QCoreApplication::tr("Blocks must be matrices"));
0210         } else
0211             ret.addError(QCoreApplication::tr("Do not want empty vectors/matrixrow elements"));
0212     } else
0213         ret.addError(QCoreApplication::tr("Matrix constructor needs vectors or matrixrow elements"));
0214     
0215     return ret;
0216 }
0217 
0218 
0219 const QString BlockDiagonalMatrixCommand::id = QStringLiteral("blockdiag");
0220 // const ExpressionType BlockDiagonalMatrixCommand::type  = variadicFunctionType(VectorAndMatrixAlternatives);
0221 const ExpressionType BlockDiagonalMatrixCommand::type  = ExpressionType(ExpressionType::Lambda)
0222 .addParameter(ExpressionType(ExpressionType::Any, 
0223                              ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1)))
0224 .addParameter(ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1));
0225 
0226 Expression BlockDiagonalMatrixCommand::operator()(const QList< Analitza::Expression >& args)
0227 {
0228     Expression ret;
0229     
0230     int nargs = args.size();
0231     bool byvector = false;
0232     
0233     if (nargs == 0) {
0234         ret.addError(QCoreApplication::tr("Invalid parameter count for '%1'").arg(BlockDiagonalMatrixCommand::id));
0235         return ret;
0236     }
0237     
0238     const Analitza::Vector *v = byvector? static_cast<const Analitza::Vector*>(args.first().tree()) : nullptr;
0239     
0240     if (byvector) nargs = v->size();
0241     
0242     for (int k = 0; k < nargs; ++k) {
0243         if ((byvector? v->at(k)->type() : args.at(k).tree()->type()) == Analitza::Object::none) {
0244             ret.addError(QCoreApplication::tr("the arg %1 is invalid or is error").arg(k+1));
0245             return ret;
0246         }
0247     }
0248     
0249     if (args.first().tree()->type() == Analitza::Object::matrix) {
0250         bool failbyblockdiag = false;
0251         int nrows = 0;
0252         int ncols = 0;
0253 
0254         for (int k = 0; k < nargs && !failbyblockdiag; ++k)
0255             if (args.at(k).tree()->type() == Analitza::Object::matrix) {
0256                 const Analitza::Matrix *block = static_cast<const Analitza::Matrix*>(args.at(k).tree());
0257                 const int m = block->rowCount();
0258                 const int n = block->columnCount();
0259                 if (m > 0 && n > 0) {
0260                     nrows += m;
0261                     ncols += n;
0262                 } else {
0263                     ret.addError(QCoreApplication::tr("Do not want empty blocks"));
0264                     failbyblockdiag = true;
0265                 }
0266             } else {
0267                 ret.addError(QCoreApplication::tr("Blocks must be matrices"));
0268                 failbyblockdiag = true;
0269             }
0270         
0271         if (!failbyblockdiag) {
0272             Analitza::Matrix *matrix = new Analitza::Matrix();
0273             QVector< QVector< const Analitza::Object* > > objmatrix(nrows, QVector< const Analitza::Object* >(ncols, nullptr));
0274             
0275             nrows = 0;
0276             ncols = 0;
0277         
0278             for (int k = 0; k < nargs; ++k) {
0279                 const Analitza::Matrix *block = static_cast<const Analitza::Matrix*>(args.at(k).tree());
0280                 const int m = block->rowCount();
0281                 const int n = block->columnCount();
0282                 
0283                 for (int i = 0; i < m; ++i)
0284                     for (int j = 0; j < n; ++j)
0285                         objmatrix[i+nrows][j+ncols] = block->at(i,j);
0286                 
0287                 nrows += m;
0288                 ncols += n;
0289             }
0290             
0291             for (int i = 0; i < nrows; ++i) {
0292                 Analitza::MatrixRow *row = new Analitza::MatrixRow(ncols);
0293                 
0294                 for (int j = 0; j < ncols; ++j) {
0295                     const Analitza::Object *obj = objmatrix[i][j];
0296                     
0297                     if (obj)
0298                         row->appendBranch(obj->copy());
0299                     else
0300                         row->appendBranch(new Analitza::Cn(0));
0301                 }
0302                 
0303                 matrix->appendBranch(row);
0304             }
0305             
0306             ret.setTree(matrix);
0307         }
0308         
0309         return ret;
0310     }
0311     
0312     return ret;
0313 }