/** @(#) numericalap/logisticnewtondogleg.cpp */ #include "logisticnewtondogleg.h" class LeastSquareFunction : public FunctionND_1D { private: int m; VectorDouble t; VectorDouble P; public: LeastSquareFunction(VectorDouble &time, VectorDouble &population) throw (invalid_argument) { if (time.getSize() == population.getSize()) { m = time.getSize(); } else { throw invalid_argument( "LeastSquareFunction: Dimensiones distintas"); } t = time; P = population; } // No se checa las dimensiones de xc y Fc. double evaluate(VectorDouble &xc) { xc.setLogic(1); // Funcion del usuario // f(x1, x2) = x1^4 + x1^2 + x2^2 // xc = [1 , 1] double a = xc[1]; double b = xc[2]; double c = xc[3]; double fc = 0.0; // ==sum int i; // Residuals // manualmente 0.5 (R^t R) for (i = 1; i <= m; ++i) { fc += square( ( a * c / ( b * c + exp(-a * t[i]) ) ) - P[i] ); } fc /= 2.0; return fc; } }; // No olvidar ";" /** * @param t Tiempo. Dim: m. * @param P Poblacion. Dim: m. * @param x Coeficientes. x[1] = a, x[2] = b, x[3] = c. Dim: 3. * @param ri return Residuos. Dim: m. * @param g return Gradiente. Dim: m. * @param J return Jacobiana. Dim: mx3. * @param H return Hessiana. Dim: mxm. */ void logisticMatrices(VectorDouble &t, VectorDouble &P, VectorDouble &xc, VectorDouble &ri, RectangularMatrix &J, VectorDouble &g, SquaredMatrix &H) { // Coeficientes double a = xc[1]; // a: crecimiento (maltusiano), double b = xc[2]; // b: competencia, double c = xc[3]; // c: parametro de integracion //(1er punto de la curva) int m = t.getSize(); int i; // Informacion de las 2as derivadas de la expansion de Taylor SquaredMatrix S(3, 1, true); // inicializar a ceros // Residuals for (i = 1; i <= m; ++i) { ri[i] = ( a * c / (b * c + exp(-a * t[i])) ) - P[i]; } // Jacobiano double eati, den2, den3; // aux for (i = 1; i <= m; ++i) { eati = exp(-a * t[i]); den2 = square((b * c) + eati); J(i, 1) = ( (b * square(c)) + ((1 + a*t[i]) * c * eati) ) / den2; J(i, 2) = (-a * square(c)) / den2; J(i, 3) = (a * eati) / den2; } // Gradiente = J^t ri RectangularMatrix Jt(3, m, 1, false); Jt.transpose(J); g = Jt * ri; // Logica cero // Hessiana // grad^2 ri. m matrices 3x3 SquaredMatrix *pHR = new SquaredMatrix[m]; for (i = 0; i < m; ++i) { //ERROR pHR[i].resize(3, 1, false); } for (i = 0; i < m; ++i) { eati = exp(-a * t[i+1]); den3 = pow((b * c) + eati, 3.0); pHR[i](1, 1) = ( (-c * t[i+1] * eati) * ( (a * b * c * t[i+1]) - (a * t[i+1] * eati) - (2 * b * c) - (2 * eati) ) ) / den3; // factorizar ( -square(c) * (b * c + eati + 2 * a * t[i] * eati)) pHR[i](1, 2) = ( -square(c) * (b * c + eati + 2 * a * t[i+1] * eati) ) / den3; pHR[i](1, 3) = ( -eati * ( (a * b * c * t[i+1]) - (a * t[i+1] * eati) - (b * c) - eati ) ) / den3; pHR[i](2, 2) = (2 * a * c*c*c) / den3; pHR[i](2, 3) = (-2 * a * c * eati) / den3; pHR[i](3, 3) = (-2 * a *b * eati) / den3; } // Calcular S. H = J^T J + S SquaredMatrix sum(3, 1, true); // sumando = 0 for (i = 0; i < m; ++i) { // sum = pHR[i] * ri[i]; SquaredMatrix::multiply(pHR[i], ri[i], sum); //S = S + sum; SquaredMatrix::add(S, sum, S); } // De triangular a simetrica S.upperTriangularToSymmetric(); // H = J^T J + S //H = J.transpose() * J + S; RectangularMatrix aux(1, 1, false); RectangularMatrix aux2(1, 1, false); aux2.transpose(J); RectangularMatrix::multiply(aux2, J, aux); RectangularMatrix::add(aux, S, H); } /** Aproximacion por diferencias finitas por el metodo de quasi Newton * Para la decision del paso elige entre elpaso de Newton o el DogLeg. * No se checan dimensiones. La dimension n se toma de la dimension de xc. * Logica uno. * @author Daniel Alba Cuellar, Omar Posada Villarreal * @version 1.0, 22/04/2002 * @param n Entero. Natural. Dimension de las matrices y vectores. * @param xc IN/OUT * IN: Vector con punto de prueba inicial. * OUT: Solucion * @param fc IN/OUT * IN: Vector con evaluacion punto de prueba inicial. * OUT: Evaluacion de la solucion * @param theFunction Nombre de la funcion. F:R^n->R^n. FVEC. * @param Sx Escala. Valores usuales. * @param maxstep Maximo taman~o de paso. Dado por QuasiNewton * @param steptol Precision del paso * @param delta in/out Radio de la region de confianza. * @param xp return x+ * @param fp return f+ * @param eta Precision de la maquina. Omision: mathpos::MACHEPS. * @param deb debug. Mostrar resultados. * 0: No mostrar. * 1: Mostrar resumen, version corta. * 2: Mostrar pasos intermedios, version larga. * @param logType * @see pages 314-315 */ /* * @param H return. Matriz cuadrada Hessiana. * @param fc return Vector sobreescrito Entrada/Salida. */ void logisticNewtonDogLeg(int n, VectorDouble &t, VectorDouble &P, VectorDouble &xc, double &fc, VectorDouble &Sx, int iterations, double maxstep, double steptol, double &delta, double eta, int logType, int waitType, string pathFile) throw (runtime_error) { // QUITAR MAX_LOG Y OTROS, CAMBIAR LA FIRMA DE LAS FUNCIONES // En depuracion usar checkMethod, al final del archivo LLOG << "\n{ logisticNewton"; // log file ofstream outFile(pathFile.data(), ios::out); // Recordar cerrar outFile.close(); if ( !outFile ) { // !sobrecargado throw runtime_error( "logisticNewtonDogLeg: Error de apertura de archivo."); } // { posada xc.setLogic(1); Sx.setLogic(1); //H.setLogic(1); // } posada SquaredMatrix H(n, 1); // Hessiana SquaredMatrix QH(n, 1); // Q de Hessiana SquaredMatrix R(n, 1); // Factoriz QR SquaredMatrix L(n, 1); // Factoriz Cholesky VectorDouble c(n, 1); // used by qrdcmp VectorDouble d(n, 1); // used by qrdcmp VectorDouble g(n, 1); // grad(f(xk)) VectorDouble xp(n, 1); // x+ VectorDouble sN(n, 1); // en el libro lo llama sk^N // param de IN/OUT, en Ax=b en IN es b, y en OUT x //sN Solucion aproximada a H(f(xk)) sk^N = - grad(f(xk)) // { logistic int m = t.getSize(); RectangularMatrix J(m, 3, 1); // Jacobiano logistic VectorDouble ri(m, 1); // Residuos LeastSquareFunction theFunction(t, P); SquaredMatrix eigenVectors(n, 1, true); //mu VectorDouble initial(n, 1); // Para iniciar el eigen de la potencia VectorDouble eVector(n, 1); // Eigen vector initial.fill(1.0); eVector.fill(1.0); // } logistic bool maxtaken; // x+ esta en el radio de la region de confinza int retcode; int i, k; int sing; double cond; double mx, num, den, aux; // condicion de paro double fp; // f+ bool Newttaken; // Encabezado SLOG << "Iter\tRadio1\tRadio2\tPaso\tfc\tdim(xc)\txc"; FLOG << "Iter\tRadio1\tRadio2\tPaso\tfc\tdim(xc)\txc"; // iteracion 0 SLOG << "\n0\t" << delta << "\t" << delta << "\tInicial" << "\t" << fc << "\t" << n << "\t" << xc; FLOG << "\n0\t" << delta << "\t" << delta << "\tInicial" << "\t" << fc << "\t" << n << "\t" << xc; fc = theFunction.evaluate(xc); // antes param // Valor tipico de f typ f double typF = theFunction.evaluate(Sx); double mu; // mu = -minEigVal(H) SquaredMatrix mu_I(3, 1, false); // newton iteration for (k=1; k<=iterations; k++) { //ERROR EN LA 2A CORRIDA logisticMatrices(t, P, xc, ri, J, g, H); // qrdcmp cambia a H, se toma una copia de H QH.copyFrom(H); // QH no es simetrica sing = qrdcmp(QH, n, c, d); // aproximacion // Si es singular, no se puede buscar la condicion. if (sing) { throw runtime_error( "\nlogisticNewtonDogLeg: La matriz es singular."); } else { //sN Solucion aproximada a H(f(xk)) sk^N = -grad(f(xk)) R.createRMatrix(QH, d); cond = R.condition(); if (cond > (1.0 / eta)) { throw runtime_error( "\nfiniteNewtonDogLeg: Hessiano mal condicionado."); } sN = g * -1.0; qrsolv(QH, n, c, d, sN); //sN in/out // Obtener triang inf de hessiana if ( !factorizeCholesky(n, H, L)) { // Perturbar la matriz. //H = H + mu I, mu > 0 // positivo * 101% // asegurarnos que no sea igual al min eigVal /* Por Gerschgori*/ mu = -H.findMinLimitEigenValue() * (1.01); //1 .05 = 1+ SQRT_MACHEPS); /* mu = fabs(H.dominantEigenValue( initial, iterations, 1, eigenVectors, eVector) ) * (1.01); /**/ // LA MU NO FUNCIONA mu_I.toIdentity(); RectangularMatrix::multiply(mu_I, mu, mu_I); RectangularMatrix::add(H, mu_I, H); if ( !factorizeCholesky(n, H, L) ) { throw runtime_error( "logisticNewtonDogLeg: No se factorizo Hessiana."); } } // delta es in/out SLOG << "\n" << k << "\t" << delta; // IN FLOG << "\n" << k << "\t" << delta; // IN // en dogdriver se actualiza x doubleDogLegDriver(n, xc, fc, theFunction, g, L, sN, Sx, maxstep, steptol, delta, retcode, xp, fp, maxtaken, Newttaken); SLOG << "\t" << delta; // OUT FLOG << "\t" << delta; // OUT if (maxtaken) { LLOG << "\nx+ esta cerca del" << " radio de la region de confinza."; } // duda con las evaluaciones de fp y fc //antes if (Newttaken) { // Usa el paso de Newton xc = xc + sN; fc = theFunction.evaluate(xc); SLOG << "\tNewton"; FLOG << "\tNewton"; } else { // Usa el doglegstep xc = xp; fc = fp; SLOG << "\tDogleg"; FLOG << "\tDogleg"; } /* 0: x+ satisfactorio encontrado * 1: la rutina fallo para encontrar una x+ satisfactoria, * suficientemente distinta de xc. */ SLOG << "\t" << fc << "\t" << n << "\t" << xc; FLOG << "\t" << fc << "\t" << n << "\t" << xc; //if (retcode == 0) { // No hacer nada if (retcode == 1) { LLOG << "\n } logisticNewtonDogLeg"; outFile.close(); return; } // Criterio de paro // Dennis. pag 160. Ecuacion (7.2.5) // max (i=[1, n]) | num/den | <= steptol // num = grad(f(x)i max{|(x+)i|, typ xi} // den = max{|f(x+)|, typ f} // typf = f(xc) den = max( fabs(fc), fabs(typF)); if (isAlmostZero(den)) { throw runtime_error( "\nlogisticNewtonDogLeg: Division entre cero."); } mx = -1; // mx debe ser positivo for (i = 1; i <= n; ++i) { num = g[i] * max( fabs(xp[i]), 1.0/Sx[i] ); aux = fabs(num / den); if (aux > mx) { mx = aux; } } LLOG << "\nCondicion de paro. mx < steptol (" << mx << " < " << steptol << "): " << toStr(mx < steptol); if (mx < steptol) { LLOG << "\n } logisticNewtonDogLeg"; outFile.close(); return; } SWAIT(); } // if } LLOG << "\n } logisticNewtonDogLeg"; outFile.close(); } /** Checa parametros: dimensiones, rangos. */ void checkLogisticNewtonDogLeg(int n, VectorDouble &t, VectorDouble &P, VectorDouble &xc, double &fc, VectorDouble &Sx, int iterations, double maxstep, double steptol, double &delta, double eta, int logType, int waitType, string pathFile) throw (invalid_argument, runtime_error) { // Condiciones invalidas if ( (n < 1) || (xc.getSize() != n) || (Sx.getSize() != n) || (iterations < 1) || (delta < 0.0) || (eta < 0.0) ) { throw invalid_argument("\nlogisticNewtonDogLeg: arg invalidos."); } // Prevee division entre cero if (Sx.hasAlmostZeros()) { throw invalid_argument( "logisticNewtonDogLeg: Division entre cero."); } logisticNewtonDogLeg(n, t, P, xc, fc, Sx, iterations, maxstep, steptol, delta, eta, logType, waitType, pathFile); } // Fin-------------------------------------------------------------------------