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 "matrixcommands.h"
0020 
0021 #include <QCoreApplication>
0022 
0023 #include "expression.h"
0024 #include "value.h"
0025 #include "matrix.h"
0026 #include "operations.h"
0027 
0028 using Analitza::Expression;
0029 using Analitza::ExpressionType;
0030 
0031 static const QString MATRIX_SIZE_ERROR_MESSAGE = QCoreApplication::tr("Matrix dimensions must be greater than zero");
0032 
0033 //BEGIN FillMatrixConstructor
0034 
0035 const QString MatrixCommand::id = QStringLiteral("matrix");
0036 const ExpressionType MatrixCommand::type = ExpressionType(ExpressionType::Lambda)
0037 .addParameter(ExpressionType(ExpressionType::Any))
0038 .addParameter(ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1));
0039 
0040 Expression MatrixCommand::operator()(const QList< Analitza::Expression >& args)
0041 {
0042     Expression ret;
0043     
0044     const int nargs = args.size();
0045     
0046     switch(nargs) {
0047         case 0: {
0048             ret.addError(QCoreApplication::tr("Invalid parameter count for '%1'").arg(MatrixCommand::id));
0049             
0050             return ret;
0051         }    break;
0052         //BEGIN matrix(m,n,v)
0053         case 1: { // build square matrix filled with zeros
0054             if (args.at(0).tree()->type() == Analitza::Object::value) {
0055                 const Analitza::Cn *nobj = static_cast<const Analitza::Cn*>(args.at(0).tree());
0056                 
0057                 if (nobj->isInteger() && nobj->value() > 0) {
0058                     ret.setTree(new Analitza::Matrix(nobj->intValue(), nobj->intValue(), new Analitza::Cn(0.)));
0059                 } else
0060                     ret.addError(MATRIX_SIZE_ERROR_MESSAGE);
0061                 
0062                 return ret;
0063             }
0064         }    break;
0065         case 2: { // build rectangular matrix filled with zeros
0066             if (args.at(0).tree()->type() == Analitza::Object::value && args.at(1).tree()->type() == Analitza::Object::value) {
0067                 const Analitza::Cn *nrowsobj = static_cast<const Analitza::Cn*>(args.at(0).tree());
0068                 const Analitza::Cn *ncolsobj = static_cast<const Analitza::Cn*>(args.at(1).tree());
0069                 
0070                 if (nrowsobj->isInteger() && ncolsobj->isInteger() && nrowsobj->value() > 0 && ncolsobj->value() > 0) {
0071                     ret.setTree(new Analitza::Matrix(nrowsobj->intValue(), ncolsobj->intValue(), new Analitza::Cn(0.)));
0072                 } else
0073                     ret.addError(MATRIX_SIZE_ERROR_MESSAGE);
0074                 
0075                 return ret;
0076             }
0077         }    break;
0078         case 3: { // build square matrix filled with a fixed value
0079             if (args.at(0).tree()->type() == Analitza::Object::value && 
0080                 args.at(1).tree()->type() == Analitza::Object::value && 
0081                 args.at(2).tree()->type() == Analitza::Object::value) {
0082                 const Analitza::Cn *nrowsobj = static_cast<const Analitza::Cn*>(args.at(0).tree());
0083                 const Analitza::Cn *ncolsobj = static_cast<const Analitza::Cn*>(args.at(1).tree());
0084                 
0085                 if (nrowsobj->isInteger() && ncolsobj->isInteger() && nrowsobj->value() > 0 && ncolsobj->value() > 0) {
0086                     ret.setTree(new Analitza::Matrix(nrowsobj->intValue(), ncolsobj->intValue(), static_cast<const Analitza::Cn*>(args.last().tree())));
0087                 } else
0088                     ret.addError(MATRIX_SIZE_ERROR_MESSAGE);
0089                 
0090                 return ret;
0091             }
0092         }    break;
0093         //END matrix(m,n,v)
0094     }
0095     
0096     Q_ASSERT(nargs > 0);
0097     Q_ASSERT(ret.toString().isEmpty());
0098     Q_ASSERT(ret.isCorrect());
0099     
0100     //BEGIN matrix(vector{...}, ...) and matrix(matrixrow{...}, ...)
0101     const Analitza::Object::ObjectType firstArgType = args.first().tree()->type();
0102     
0103     if (firstArgType == Analitza::Object::vector || firstArgType == Analitza::Object::matrixrow) {
0104         const bool isVector = (firstArgType == Analitza::Object::vector);
0105         const Analitza::Vector *firstVector = static_cast<const Analitza::Vector*>(args.first().tree());
0106         
0107         if (firstVector->size() > 0) { // we will check this for all vectors later
0108             const int firstVectorSize = firstVector->size();
0109             
0110             bool iscorrect = true; // assumes all are rows
0111             Analitza::Matrix *matrix = new Analitza::Matrix();
0112             
0113             for (int i = 0; i < nargs && iscorrect; ++i) {
0114                 if (args.at(i).tree()->type() == firstArgType)
0115                 {
0116                     const Analitza::MatrixRow *row = static_cast<const Analitza::MatrixRow*>(args.at(i).tree());
0117                     
0118                     if (row->size() == firstVectorSize)
0119                         matrix->appendBranch(row->copy());
0120                     else {
0121                         iscorrect = false;
0122                         ret.addError(QCoreApplication::tr("All matrixrow elements must have the same size"));
0123                     }
0124                 }
0125                 else {
0126                     iscorrect = false;
0127                     ret.addError(QCoreApplication::tr("Not all are rows or vectors"));
0128                 }
0129             }
0130             
0131             if (!ret.isCorrect())
0132                 delete matrix;
0133             else if (iscorrect) {
0134                 if (isVector) {
0135                     QString* error=nullptr;
0136                     ret.setTree(Analitza::Operations::reduceUnary(Analitza::Operator::transpose, matrix, &error));
0137                     delete matrix;
0138                 
0139                     Q_ASSERT(error == nullptr);
0140                 } else
0141                     ret.setTree(matrix);
0142             } else {
0143                 ret.addError(QCoreApplication::tr("Every argument must be a matrixrow element"));
0144                 delete matrix;
0145             }
0146         } else
0147             ret.addError(QCoreApplication::tr("Do not want empty vectors/matrixrow elements"));
0148     } else
0149         ret.addError(QCoreApplication::tr("Matrix constructor needs vectors or matrixrow elements"));
0150     //END matrix(vector{...}, ...) and matrix(matrixrow{...}, ...)
0151     
0152     return ret;
0153 }
0154 
0155 //END FillMatrixConstructor
0156 
0157 
0158 //BEGIN IdentityMatrixConstructor
0159 
0160 const QString IdentityMatrixCommand::id = QStringLiteral("identitymatrix");
0161 const ExpressionType IdentityMatrixCommand::type = ExpressionType(ExpressionType::Lambda)
0162 .addParameter(ExpressionType(ExpressionType::Value))
0163 .addParameter(ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1));
0164 
0165 Expression IdentityMatrixCommand::operator()(const QList< Analitza::Expression >& args)
0166 {
0167     Expression ret;
0168     
0169     const Analitza::Cn *nobj = static_cast<const Analitza::Cn*>(args.first().tree());
0170     const int n = nobj->value();
0171     
0172     if (nobj->isInteger() && n > 0) {
0173         ret.setTree(Analitza::Matrix::identity(n));
0174     } else
0175         ret.addError(MATRIX_SIZE_ERROR_MESSAGE);
0176 
0177     return ret;
0178 }
0179 
0180 //END IdentityMatrixConstructor
0181 
0182 
0183 //BEGIN DiagonalMatrixConstructor
0184 
0185 const QString DiagonalMatrixCommand::id = QStringLiteral("diag");
0186 const ExpressionType DiagonalMatrixCommand::type  = ExpressionType(ExpressionType::Lambda)
0187 .addParameter(ExpressionType(ExpressionType::Any))
0188 .addParameter(ExpressionType(ExpressionType::Many, QList<ExpressionType>() 
0189 << ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -1) 
0190 << ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1)));
0191 
0192 Expression DiagonalMatrixCommand::operator()(const QList< Analitza::Expression >& args)
0193 {
0194     Expression ret;
0195     
0196     int nargs = args.size();
0197     bool byvector = false;
0198     
0199     switch(nargs) {
0200         case 0: {
0201             ret.addError(QCoreApplication::tr("Invalid parameter count for '%1'").arg(DiagonalMatrixCommand::id));
0202             
0203             return ret;
0204         }    break;
0205         //BEGIN diag(matrix{...}, diagindex)
0206         case 1: {
0207             if (args.first().tree()->type() == Analitza::Object::matrix) {
0208                 const Analitza::Matrix *matrix = static_cast<const Analitza::Matrix*>(args.first().tree());
0209                 const int n = std::min(matrix->rowCount(), matrix->columnCount());
0210                 
0211                 Analitza::Vector *diagonal = new Analitza::Vector(n);
0212                 
0213                 for (int i = 0; i < n; ++i)
0214                     diagonal->appendBranch(matrix->at(i, i)->copy());
0215                 
0216                 ret.setTree(diagonal);
0217                 
0218                 return ret;
0219             } else if (args.first().tree()->type() == Analitza::Object::vector)
0220                 byvector = true;
0221         }    break;
0222         case 2: {
0223             if (args.first().tree()->type() == Analitza::Object::matrix && args.last().tree()->type() == Analitza::Object::value) {
0224                 const Analitza::Cn *nobj = static_cast<const Analitza::Cn*>(args.last().tree());
0225                 
0226                 if (nobj->isInteger()) {
0227                     const Analitza::Matrix *matrix = static_cast<const Analitza::Matrix*>(args.first().tree());
0228                     const int nrows = matrix->rowCount();
0229                     const int ncols = matrix->columnCount();
0230                     const int npos = nobj->value();
0231                     const int absnpos = std::abs(npos);
0232                     const int absnpos1 = absnpos + 1;
0233                     const bool isneg = npos < 0;
0234                     
0235                     int n = 0; // or until/to
0236                     int rowoffset = 0;
0237                     int coloffset = 0;
0238                     
0239                     if (isneg) {
0240                         if (absnpos1 > nrows) {
0241                             ret.addError(QCoreApplication::tr("The nth diagonal index must be less than the row count"));
0242                             return ret;
0243                         }
0244                         
0245                         n = std::min(nrows - absnpos, ncols);
0246                         rowoffset = absnpos;
0247                     } else { // square matrix case too
0248                         if (absnpos1 > ncols) {
0249                             ret.addError(QCoreApplication::tr("The nth diagonal index must be less than the column count"));
0250                             return ret;
0251                         }
0252                         
0253                         n = std::min(nrows, ncols - absnpos);
0254                         coloffset = absnpos;
0255                     }
0256                     
0257                     Analitza::Vector *diagonal = new Analitza::Vector(n);
0258                     
0259                     for (int i = 0; i < n; ++i)
0260                         diagonal->appendBranch(matrix->at(rowoffset + i, coloffset + i)->copy());
0261                         
0262                     ret.setTree(diagonal);
0263                 }
0264                 else
0265                     ret.addError(QCoreApplication::tr("nth diagonal index must be integer number"));
0266                 
0267                 return ret;
0268             } else if (args.last().tree()->type() != Analitza::Object::value && args.last().tree()->type() != Analitza::Object::matrix) {
0269                 ret.addError(QCoreApplication::tr("nth diagonal index must be a positive integer number"));
0270                 
0271                 return ret;
0272             }
0273         }    break;
0274         //END diag(matrix{...}, diagindex)
0275     }
0276     
0277     Q_ASSERT(nargs > 0);
0278     Q_ASSERT(ret.toString().isEmpty());
0279     Q_ASSERT(ret.isCorrect());
0280     
0281     //BEGIN diag(a,b, ...) or diag(vector{a,b, ...})
0282     const Analitza::Vector *v = byvector? static_cast<const Analitza::Vector*>(args.first().tree()) : nullptr;
0283     
0284     if (byvector) nargs = v->size();
0285     
0286     for (int k = 0; k < nargs; ++k)
0287         if ((byvector? v->at(k)->type() : args.at(k).tree()->type()) == Analitza::Object::none) {
0288             ret.addError(QCoreApplication::tr("the arg %1 is invalid or is error").arg(k+1));
0289             return ret;
0290         }
0291     
0292     Analitza::Matrix *matrix = new Analitza::Matrix();
0293     
0294     for (int i = 0; i < nargs; ++i) {
0295         Analitza::MatrixRow *row = new Analitza::MatrixRow(nargs);
0296         
0297         for (int j = 0; j < nargs; ++j)
0298             if (i == j)
0299                 row->appendBranch(byvector? v->at(j)->copy() : args.at(j).tree()->copy());
0300             else
0301                 row->appendBranch(new Analitza::Cn(0));
0302         
0303         matrix->appendBranch(row);
0304     }
0305     
0306     ret.setTree(matrix);
0307     //END diag(a,b, ...) or diag(vector{a,b, ...})
0308     
0309     return ret;
0310 }
0311 
0312 //END DiagonalMatrixConstructor
0313 
0314 
0315 //BEGIN TridiagonalMatrixConstructor
0316 
0317 const QString TridiagonalMatrixCommand::id = QStringLiteral("tridiag");
0318 const ExpressionType TridiagonalMatrixCommand::type = ExpressionType(ExpressionType::Lambda)
0319 .addParameter(ExpressionType(ExpressionType::Value))
0320 .addParameter(ExpressionType(ExpressionType::Value))
0321 .addParameter(ExpressionType(ExpressionType::Value))
0322 .addParameter(ExpressionType(ExpressionType::Value))
0323 .addParameter(ExpressionType(ExpressionType::Matrix, ExpressionType(ExpressionType::Vector, ExpressionType(ExpressionType::Value), -2), -1));
0324 
0325 //TODO create rectangular matrices too 
0326 Expression TridiagonalMatrixCommand::operator()(const QList< Analitza::Expression >& args)
0327 {
0328     Expression ret;
0329     
0330     const Analitza::Cn *nobj = static_cast<const Analitza::Cn*>(args.last().tree());
0331     const int n = nobj->value();
0332     
0333     if (nobj->isInteger() && n > 0) {
0334         Analitza::Matrix *matrix = new Analitza::Matrix();
0335         
0336         for (int row = 0; row < n; ++row) {
0337             Analitza::MatrixRow *rowobj = new Analitza::MatrixRow(n);
0338             
0339             for (int col= 0; col < n; ++col)
0340                 if (row == col + 1) // a
0341                     rowobj->appendBranch(args.at(0).tree()->copy());
0342                 else
0343                     if (row == col) // b
0344                         rowobj->appendBranch(args.at(1).tree()->copy());
0345                     else
0346                         if (row == col - 1) // c
0347                             rowobj->appendBranch(args.at(2).tree()->copy());
0348                         else
0349                             rowobj->appendBranch(new Analitza::Cn(0));
0350             
0351             matrix->appendBranch(rowobj);
0352         }
0353         
0354         ret.setTree(matrix);
0355     } else
0356         ret.addError(MATRIX_SIZE_ERROR_MESSAGE);
0357     
0358     return ret;
0359 }
0360 
0361 //END TridiagonalMatrixConstructor