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