// Function definitions. // ----------------------------------------------------------------- void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { //Input Args user_function_data fun; double *x0, *ydata = NULL, *lb = NULL, *ub = NULL, *A = NULL, *b = NULL, *Aeq = NULL, *beq = NULL; //Options int maxIter = 500; double info[LM_INFO_SZ]; double opts[LM_OPTS_SZ]={J_INIT_MU, J_STOP_THRESH, J_STOP_THRESH, J_STOP_THRESH, LM_DIFF_DELTA}; //Outputs Args double *x, *fval, *exitflag, *iter, *feval; double *pcovar = NULL; //Internal Vars size_t ndec, ndat; int i, status, havJac = 0, conMode = 0; int nineq=0, neq=0; double *covar = NULL; double *Apr, *bpr; double *llb, *lub; citer = 1; iterF.enabled = false; if (nrhs < 1) { if(nlhs < 1) printSolverInfo(); else plhs[0] = mxCreateString(LM_VERSION); return; } //Check user inputs & get constraint information checkInputs(prhs,nrhs,&conMode); //Get Sizes ndec = mxGetNumberOfElements(prhs[2]); ndat = mxGetNumberOfElements(prhs[3]); //Get Objective Function Handle if (mxIsChar(prhs[0])) { CHECK(mxGetString(prhs[0], fun.f, FLEN) == 0,"error reading objective name string"); fun.nrhs = 1; fun.xrhs = 0; } else { fun.prhs[0] = (mxArray*)prhs[0]; strcpy(fun.f, "feval"); fun.nrhs = 2; fun.xrhs = 1; } fun.prhs[fun.xrhs] = mxCreateDoubleMatrix(ndec, 1, mxREAL); //x0 fun.print = 0; //Check and Get Gradient Function Handle if(!mxIsEmpty(prhs[1])) { havJac = 1; if (mxIsChar(prhs[1])) { CHECK(mxGetString(prhs[1], fun.g, FLEN) == 0,"error reading gradient name string"); fun.nrhs_g = 1; fun.xrhs_g = 0; } else { fun.prhs_g[0] = (mxArray*)prhs[1]; strcpy(fun.g, "feval"); fun.nrhs_g = 2; fun.xrhs_g = 1; } fun.prhs_g[fun.xrhs_g] = mxCreateDoubleMatrix(ndec, 1, mxREAL); //x0 } //Get x0 + data x0 = mxGetPr(prhs[2]); ydata = mxGetPr(prhs[3]); fun.ydata = ydata; //Get Bounds if(conMode & 1) { //LB if(!mxIsEmpty(prhs[4])){ llb = mxGetPr(prhs[4]); lb = mxCalloc(ndec,sizeof(double)); memcpy(lb,llb,ndec*sizeof(double)); for(i=0;i<ndec;i++) { if(mxIsInf(lb[i])) lb[i] = -DBL_MAX; } } else { lb = mxCalloc(ndec,sizeof(double)); for(i=0;i<ndec;i++) lb[i] = -DBL_MAX; } //UB if(nrhs > 5 && !mxIsEmpty(prhs[5])){ lub = mxGetPr(prhs[5]); ub = mxCalloc(ndec,sizeof(double)); memcpy(ub,lub,ndec*sizeof(double)); for(i=0;i<ndec;i++) { if(mxIsInf(ub[i])) ub[i] = DBL_MAX; } } else { ub = mxCalloc(ndec,sizeof(double)); for(i=0;i<ndec;i++) ub[i] = DBL_MAX; } } //Get Linear Inequality Constraints if(conMode & 2) { nineq = (int)mxGetM(prhs[7]); Apr = mxGetPr(prhs[6]); bpr = mxGetPr(prhs[7]); //Need to flip >= to <= A = mxCalloc(ndec*nineq,sizeof(double)); b = mxCalloc(nineq,sizeof(double)); for(i=0;i<ndec*nineq;i++) A[i] = -Apr[i]; for(i=0;i<nineq;i++) b[i] = -bpr[i]; } //Get Linear Equality Constraints if(conMode & 4) { Aeq = mxGetPr(prhs[8]); beq = mxGetPr(prhs[9]); neq = (int)mxGetM(prhs[9]); } //Get Options if specified if(nrhs > 10) { if(mxGetField(prhs[10],0,"maxiter")) maxIter = (int)*mxGetPr(mxGetField(prhs[10],0,"maxiter")); if(mxGetField(prhs[10],0,"display")) fun.print = (int)*mxGetPr(mxGetField(prhs[10],0,"display")); if(mxGetField(prhs[10],0,"iterfun") && !mxIsEmpty(mxGetField(prhs[10],0,"iterfun"))) { iterF.prhs[0] = (mxArray*)mxGetField(prhs[10],0,"iterfun"); strcpy(iterF.f, "feval"); iterF.enabled = true; iterF.prhs[1] = mxCreateNumericMatrix(1,1,mxINT32_CLASS,mxREAL); iterF.prhs[2] = mxCreateDoubleMatrix(1,1,mxREAL); iterF.prhs[3] = mxCreateDoubleMatrix(ndec,1,mxREAL); } } //Create Outputs plhs[0] = mxCreateDoubleMatrix(ndec,1, mxREAL); plhs[1] = mxCreateDoubleMatrix(1,1, mxREAL); plhs[2] = mxCreateDoubleMatrix(1,1, mxREAL); plhs[3] = mxCreateDoubleMatrix(1,1, mxREAL); plhs[4] = mxCreateDoubleMatrix(1,1, mxREAL); x = mxGetPr(plhs[0]); fval = mxGetPr(plhs[1]); exitflag = mxGetPr(plhs[2]); iter = mxGetPr(plhs[3]); feval = mxGetPr(plhs[4]); //Copy initial guess to x memcpy(x,x0,ndec*sizeof(double)); //Create Covariance Matrix if Required if(nlhs>4) covar=mxCalloc(ndec*ndec,sizeof(double)); //Print Header if(fun.print) { mexPrintf("\n------------------------------------------------------------------\n"); mexPrintf(" This is LEVMAR v2.5\n"); mexPrintf(" Author: Manolis Lourakis\n MEX Interface J. Currie 2011\n\n"); mexPrintf(" Problem Properties:\n"); mexPrintf(" # Decision Variables: %4d\n",ndec); mexPrintf(" # Data Points: %4d\n",ndat); mexPrintf("------------------------------------------------------------------\n"); } //Solve based on constraints switch(conMode) { case MIN_UNCONSTRAINED: //mexPrintf("Unc Problem\n"); if(havJac) status = dlevmar_der(func, jac, x, ydata, (int)ndec, (int)ndat, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_dif(func, x, ydata, (int)ndec, (int)ndat, maxIter, opts, info, NULL, covar, &fun); break; case MIN_CONSTRAINED_BC: //mexPrintf("Box Constrained Problem\n"); if(havJac) status = dlevmar_bc_der(func, jac, x, ydata, (int)ndec, (int)ndat, lb, ub, NULL, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_bc_dif(func, x, ydata, (int)ndec, (int)ndat, lb, ub, NULL, maxIter, opts, info, NULL, covar, &fun); break; case MIN_CONSTRAINED_LIC: //mexPrintf("Linear Inequality Problem\n"); if(havJac) status = dlevmar_lic_der(func, jac, x, ydata, (int)ndec, (int)ndat, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_lic_dif(func, x, ydata, (int)ndec, (int)ndat, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); break; case MIN_CONSTRAINED_BLIC: //mexPrintf("Boxed Linear Inequality Problem\n"); if(havJac) status = dlevmar_blic_der(func, jac, x, ydata, (int)ndec, (int)ndat, lb, ub, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_blic_dif(func, x, ydata, (int)ndec, (int)ndat, lb, ub, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); break; case MIN_CONSTRAINED_LEC: //mexPrintf("Linear Equality Problem\n"); if(havJac) status = dlevmar_lec_der(func, jac, x, ydata, (int)ndec, (int)ndat, Aeq, beq, neq, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_lec_dif(func, x, ydata, (int)ndec, (int)ndat, Aeq, beq, neq, maxIter, opts, info, NULL, covar, &fun); break; case MIN_CONSTRAINED_BLEC: //mexPrintf("Boxed Linear Equality Problem\n"); if(havJac) status = dlevmar_blec_der(func, jac, x, ydata, (int)ndec, (int)ndat, lb, ub, Aeq, beq, neq, NULL, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_blec_dif(func, x, ydata, (int)ndec, (int)ndat, lb, ub, Aeq, beq, neq, NULL, maxIter, opts, info, NULL, covar, &fun); break; case MIN_CONSTRAINED_LEIC: //mexPrintf("Linear Inequality + Equality Problem\n"); if(havJac) status = dlevmar_leic_der(func, jac, x, ydata, (int)ndec, (int)ndat, Aeq, beq, neq, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_leic_dif(func, x, ydata, (int)ndec, (int)ndat, Aeq, beq, neq, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); break; case MIN_CONSTRAINED_BLEIC: //mexPrintf("Boxed Linear Inequality + Equality Problem\n"); if(havJac) status = dlevmar_bleic_der(func, jac, x, ydata, (int)ndec, (int)ndat, lb, ub, Aeq, beq, neq, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); else status = dlevmar_bleic_dif(func, x, ydata, (int)ndec, (int)ndat, lb, ub, Aeq, beq, neq, A, b, nineq, maxIter, opts, info, NULL, covar, &fun); break; default: mexErrMsgTxt("Unknown constraint configuration"); } //Save Status & Iterations *fval = info[1]; *exitflag = getStatus(info[6]); *iter = (double)status; *feval = (double)citer; //Save Covariance if Required if(nlhs > 5) { plhs[5] = mxCreateDoubleMatrix(ndec, ndec, mxREAL); pcovar = mxGetPr(plhs[5]); memcpy(pcovar,covar,ndec*ndec*sizeof(double)); } //Print Header if(fun.print){ //Termination Detected if(*exitflag == 1) mexPrintf("\n *** SUCCESSFUL TERMINATION ***\n"); else if(*exitflag == 0) mexPrintf("\n *** MAXIMUM ITERATIONS REACHED ***\n"); else if(*exitflag == -1) mexPrintf("\n *** TERMINATION: TOLERANCE TOO SMALL ***\n"); else if(*exitflag == -2) mexPrintf("\n *** TERMINATION: ROUTINE ERROR ***\n"); if(*exitflag==1) mexPrintf(" Final SSE: %12.5g\n In %3.0f iterations\n",*fval,*iter); mexPrintf("------------------------------------------------------------------\n\n"); } //Clean Up if(lb) mxFree(lb); if(ub) mxFree(ub); if(covar) mxFree(covar); if(A) mxFree(A); if(b) mxFree(b); }
int main() { register int i, j; int problem, ret; double p[5], // 5 is max(2, 3, 5) x[16]; // 16 is max(2, 3, 5, 6, 16) int m, n; double opts[LM_OPTS_SZ], info[LM_INFO_SZ]; char *probname[]={ "Rosenbrock function", "modified Rosenbrock problem", "Powell's function", "Wood's function", "Meyer's (reformulated) problem", "Osborne's problem", "helical valley function", "Boggs & Tolle's problem #3", "Hock - Schittkowski problem #28", "Hock - Schittkowski problem #48", "Hock - Schittkowski problem #51", "Hock - Schittkowski problem #01", "Hock - Schittkowski modified problem #21", "hatfldb problem", "hatfldc problem", "equilibrium combustion problem", "Hock - Schittkowski modified #1 problem #52", "Schittkowski modified problem #235", "Boggs & Tolle modified problem #7", "Hock - Schittkowski modified #2 problem #52", "Hock - Schittkowski modified problem #76", }; opts[0]=LM_INIT_MU; opts[1]=1E-15; opts[2]=1E-15; opts[3]=1E-20; opts[4]= LM_DIFF_DELTA; // relevant only if the Jacobian is approximated using finite differences; specifies forward differencing //opts[4]=-LM_DIFF_DELTA; // specifies central differencing to approximate Jacobian; more accurate but more expensive to compute! /* uncomment the appropriate line below to select a minimization problem */ problem= //0; // Rosenbrock function //1; // modified Rosenbrock problem //2; // Powell's function //3; // Wood's function 4; // Meyer's (reformulated) problem //5; // Osborne's problem //6; // helical valley function #ifdef HAVE_LAPACK //7; // Boggs & Tolle's problem 3 //8; // Hock - Schittkowski problem 28 //9; // Hock - Schittkowski problem 48 //10; // Hock - Schittkowski problem 51 #else // no LAPACK #ifdef _MSC_VER #pragma message("LAPACK not available, some test problems cannot be used") #else #warning LAPACK not available, some test problems cannot be used #endif // _MSC_VER #endif /* HAVE_LAPACK */ //11; // Hock - Schittkowski problem 01 //12; // Hock - Schittkowski modified problem 21 //13; // hatfldb problem //14; // hatfldc problem //15; // equilibrium combustion problem #ifdef HAVE_LAPACK //16; // Hock - Schittkowski modified #1 problem 52 //17; // Schittkowski modified problem 235 //18; // Boggs & Tolle modified problem #7 //19; // Hock - Schittkowski modified #2 problem 52 //20; // Hock - Schittkowski modified problem #76" #endif /* HAVE_LAPACK */ switch(problem){ default: fprintf(stderr, "unknown problem specified (#%d)! Note that some minimization problems require LAPACK.\n", problem); exit(1); break; case 0: /* Rosenbrock function */ m=2; n=2; p[0]=-1.2; p[1]=1.0; for(i=0; i<n; i++) x[i]=0.0; ret=dlevmar_der(ros, jacros, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian //ret=dlevmar_dif(ros, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // no Jacobian break; case 1: /* modified Rosenbrock problem */ m=2; n=3; p[0]=-1.2; p[1]=1.0; for(i=0; i<n; i++) x[i]=0.0; ret=dlevmar_der(modros, jacmodros, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian //ret=dlevmar_dif(modros, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // no Jacobian break; case 2: /* Powell's function */ m=2; n=2; p[0]=3.0; p[1]=1.0; for(i=0; i<n; i++) x[i]=0.0; ret=dlevmar_der(powell, jacpowell, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian //ret=dlevmar_dif(powell, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // no Jacobian break; case 3: /* Wood's function */ m=4; n=6; p[0]=-3.0; p[1]=-1.0; p[2]=-3.0; p[3]=-1.0; for(i=0; i<n; i++) x[i]=0.0; ret=dlevmar_dif(wood, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // no Jacobian break; case 4: /* Meyer's data fitting problem */ m=3; n=16; p[0]=8.85; p[1]=4.0; p[2]=2.5; x[0]=34.780; x[1]=28.610; x[2]=23.650; x[3]=19.630; x[4]=16.370; x[5]=13.720; x[6]=11.540; x[7]=9.744; x[8]=8.261; x[9]=7.030; x[10]=6.005; x[11]=5.147; x[12]=4.427; x[13]=3.820; x[14]=3.307; x[15]=2.872; //ret=dlevmar_der(meyer, jacmeyer, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian { double *work, *covar; work=malloc((LM_DIF_WORKSZ(m, n)+m*m)*sizeof(double)); if(!work){ fprintf(stderr, "memory allocation request failed in main()\n"); exit(1); } covar=work+LM_DIF_WORKSZ(m, n); ret=dlevmar_dif(meyer, p, x, m, n, 1000, opts, info, work, covar, NULL); // no Jacobian, caller allocates work memory, covariance estimated printf("Covariance of the fit:\n"); for(i=0; i<m; ++i){ for(j=0; j<m; ++j) printf("%g ", covar[i*m+j]); printf("\n"); } printf("\n"); free(work); } /* uncomment the following block to verify Jacobian */ /* { double err[16]; dlevmar_chkjac(meyer, jacmeyer, p, m, n, NULL, err); for(i=0; i<n; ++i) printf("gradient %d, err %g\n", i, err[i]); } */ break; case 5: /* Osborne's data fitting problem */ { double x33[]={ 8.44E-1, 9.08E-1, 9.32E-1, 9.36E-1, 9.25E-1, 9.08E-1, 8.81E-1, 8.5E-1, 8.18E-1, 7.84E-1, 7.51E-1, 7.18E-1, 6.85E-1, 6.58E-1, 6.28E-1, 6.03E-1, 5.8E-1, 5.58E-1, 5.38E-1, 5.22E-1, 5.06E-1, 4.9E-1, 4.78E-1, 4.67E-1, 4.57E-1, 4.48E-1, 4.38E-1, 4.31E-1, 4.24E-1, 4.2E-1, 4.14E-1, 4.11E-1, 4.06E-1}; m=5; n=33; p[0]=0.5; p[1]=1.5; p[2]=-1.0; p[3]=1.0E-2; p[4]=2.0E-2; ret=dlevmar_der(osborne, jacosborne, p, x33, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian //ret=dlevmar_dif(osborne, p, x33, m, n, 1000, opts, info, NULL, NULL, NULL); // no Jacobian } break; case 6: /* helical valley function */ m=3; n=3; p[0]=-1.0; p[1]=0.0; p[2]=0.0; for(i=0; i<n; i++) x[i]=0.0; ret=dlevmar_der(helval, jachelval, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian //ret=dlevmar_dif(helval, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // no Jacobian break; #ifdef HAVE_LAPACK case 7: /* Boggs-Tolle problem 3 */ m=5; n=5; p[0]=2.0; p[1]=2.0; p[2]=2.0; p[3]=2.0; p[4]=2.0; for(i=0; i<n; i++) x[i]=0.0; { double A[3*5]={1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, -2.0, 0.0, 1.0, 0.0, 0.0, -1.0}, b[3]={0.0, 0.0, 0.0}; ret=dlevmar_lec_der(bt3, jacbt3, p, x, m, n, A, b, 3, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, analytic Jacobian //ret=dlevmar_lec_dif(bt3, p, x, m, n, A, b, 3, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, no Jacobian } break; case 8: /* Hock - Schittkowski problem 28 */ m=3; n=3; p[0]=-4.0; p[1]=1.0; p[2]=1.0; for(i=0; i<n; i++) x[i]=0.0; { double A[1*3]={1.0, 2.0, 3.0}, b[1]={1.0}; ret=dlevmar_lec_der(hs28, jachs28, p, x, m, n, A, b, 1, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, analytic Jacobian //ret=dlevmar_lec_dif(hs28, p, x, m, n, A, b, 1, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, no Jacobian } break; case 9: /* Hock - Schittkowski problem 48 */ m=5; n=5; p[0]=3.0; p[1]=5.0; p[2]=-3.0; p[3]=2.0; p[4]=-2.0; for(i=0; i<n; i++) x[i]=0.0; { double A[2*5]={1.0, 1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, -2.0, -2.0}, b[2]={5.0, -3.0}; ret=dlevmar_lec_der(hs48, jachs48, p, x, m, n, A, b, 2, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, analytic Jacobian //ret=dlevmar_lec_dif(hs48, p, x, m, n, A, b, 2, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, no Jacobian } break; case 10: /* Hock - Schittkowski problem 51 */ m=5; n=5; p[0]=2.5; p[1]=0.5; p[2]=2.0; p[3]=-1.0; p[4]=0.5; for(i=0; i<n; i++) x[i]=0.0; { double A[3*5]={1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, -2.0, 0.0, 1.0, 0.0, 0.0, -1.0}, b[3]={4.0, 0.0, 0.0}; ret=dlevmar_lec_der(hs51, jachs51, p, x, m, n, A, b, 3, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, analytic Jacobian //ret=dlevmar_lec_dif(hs51, p, x, m, n, A, b, 3, 1000, opts, info, NULL, NULL, NULL); // lin. constraints, no Jacobian } break; #endif /* HAVE_LAPACK */ case 11: /* Hock - Schittkowski problem 01 */ m=2; n=2; p[0]=-2.0; p[1]=1.0; for(i=0; i<n; i++) x[i]=0.0; //ret=dlevmar_der(hs01, jachs01, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian { double lb[2], ub[2]; lb[0]=-DBL_MAX; lb[1]=-1.5; ub[0]=ub[1]=DBL_MAX; ret=dlevmar_bc_der(hs01, jachs01, p, x, m, n, lb, ub, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian } break; case 12: /* Hock - Schittkowski (modified) problem 21 */ m=2; n=2; p[0]=-1.0; p[1]=-1.0; for(i=0; i<n; i++) x[i]=0.0; //ret=dlevmar_der(hs21, jachs21, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian { double lb[2], ub[2]; lb[0]=2.0; lb[1]=-50.0; ub[0]=50.0; ub[1]=50.0; ret=dlevmar_bc_der(hs21, jachs21, p, x, m, n, lb, ub, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian } break; case 13: /* hatfldb problem */ m=4; n=4; p[0]=p[1]=p[2]=p[3]=0.1; for(i=0; i<n; i++) x[i]=0.0; //ret=dlevmar_der(hatfldb, jachatfldb, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian { double lb[4], ub[4]; lb[0]=lb[1]=lb[2]=lb[3]=0.0; ub[0]=ub[2]=ub[3]=DBL_MAX; ub[1]=0.8; ret=dlevmar_bc_der(hatfldb, jachatfldb, p, x, m, n, lb, ub, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian } break; case 14: /* hatfldc problem */ m=4; n=4; p[0]=p[1]=p[2]=p[3]=0.9; for(i=0; i<n; i++) x[i]=0.0; //ret=dlevmar_der(hatfldc, jachatfldc, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian { double lb[4], ub[4]; lb[0]=lb[1]=lb[2]=lb[3]=0.0; ub[0]=ub[1]=ub[2]=ub[3]=10.0; ret=dlevmar_bc_der(hatfldc, jachatfldc, p, x, m, n, lb, ub, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian } break; case 15: /* equilibrium combustion problem */ m=5; n=5; p[0]=p[1]=p[2]=p[3]=p[4]=0.0001; for(i=0; i<n; i++) x[i]=0.0; //ret=dlevmar_der(combust, jaccombust, p, x, m, n, 1000, opts, info, NULL, NULL, NULL); // with analytic Jacobian { double lb[5], ub[5]; lb[0]=lb[1]=lb[2]=lb[3]=lb[4]=0.0001; ub[0]=ub[1]=ub[2]=ub[3]=ub[4]=100.0; ret=dlevmar_bc_der(combust, jaccombust, p, x, m, n, lb, ub, 5000, opts, info, NULL, NULL, NULL); // with analytic Jacobian } break; #ifdef HAVE_LAPACK case 16: /* Hock - Schittkowski modified #1 problem 52 */ m=5; n=4; p[0]=2.0; p[1]=2.0; p[2]=2.0; p[3]=2.0; p[4]=2.0; for(i=0; i<n; i++) x[i]=0.0; { double A[3*5]={1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, -2.0, 0.0, 1.0, 0.0, 0.0, -1.0}, b[3]={0.0, 0.0, 0.0}; double lb[5], ub[5]; double weights[5]={2000.0, 2000.0, 2000.0, 2000.0, 2000.0}; // penalty terms weights lb[0]=-0.09; lb[1]=0.0; lb[2]=-DBL_MAX; lb[3]=-0.2; lb[4]=0.0; ub[0]=DBL_MAX; ub[1]=0.3; ub[2]=0.25; ub[3]=0.3; ub[4]=0.3; ret=dlevmar_blec_der(mod1hs52, jacmod1hs52, p, x, m, n, lb, ub, A, b, 3, weights, 1000, opts, info, NULL, NULL, NULL); // box & lin. constraints, analytic Jacobian //ret=dlevmar_blec_dif(mod1hs52, p, x, m, n, lb, ub, A, b, 3, weights, 1000, opts, info, NULL, NULL, NULL); // box & lin. constraints, no Jacobian } break; case 17: /* Schittkowski modified problem 235 */ m=3; n=2; p[0]=-2.0; p[1]=3.0; p[2]=1.0; for(i=0; i<n; i++) x[i]=0.0; { double A[2*3]={1.0, 0.0, 1.0, 0.0, 1.0, -4.0}, b[2]={-1.0, 0.0}; double lb[3], ub[3]; lb[0]=-DBL_MAX; lb[1]=0.1; lb[2]=0.7; ub[0]=DBL_MAX; ub[1]=2.9; ub[2]=DBL_MAX; ret=dlevmar_blec_der(mods235, jacmods235, p, x, m, n, lb, ub, A, b, 2, NULL, 1000, opts, info, NULL, NULL, NULL); // box & lin. constraints, analytic Jacobian //ret=dlevmar_blec_dif(mods235, p, x, m, n, lb, ub, A, b, 2, NULL, 1000, opts, info, NULL, NULL, NULL); // box & lin. constraints, no Jacobian } break; case 18: /* Boggs & Tolle modified problem 7 */ m=5; n=5; p[0]=-2.0; p[1]=1.0; p[2]=1.0; p[3]=1.0; p[4]=1.0; for(i=0; i<n; i++) x[i]=0.0; { double A[3*5]={1.0, 1.0, -1.0, 0.0, 0.0, 1.0, 1.0, 0.0, -1.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0}, b[3]={1.0, 0.0, 0.5}; double lb[5], ub[5]; lb[0]=-DBL_MAX; lb[1]=-DBL_MAX; lb[2]=-DBL_MAX; lb[3]=-DBL_MAX; lb[4]=-0.3; ub[0]=0.7; ub[1]= DBL_MAX; ub[2]= DBL_MAX; ub[3]= DBL_MAX; ub[4]=DBL_MAX; ret=dlevmar_blec_der(modbt7, jacmodbt7, p, x, m, n, lb, ub, A, b, 3, NULL, 1000, opts, info, NULL, NULL, NULL); // box & lin. constraints, analytic Jacobian //ret=dlevmar_blec_dif(modbt7, p, x, m, n, lb, ub, A, b, 3, NULL, 10000, opts, info, NULL, NULL, NULL); // box & lin. constraints, no Jacobian } break; case 19: /* Hock - Schittkowski modified #2 problem 52 */ m=5; n=5; p[0]=2.0; p[1]=2.0; p[2]=2.0; p[3]=2.0; p[4]=2.0; for(i=0; i<n; i++) x[i]=0.0; { double C[3*5]={1.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, -2.0, 0.0, -1.0, 0.0, 0.0, 1.0}, d[3]={-1.0, -2.0, -7.0}; ret=dlevmar_bleic_der(mod2hs52, jacmod2hs52, p, x, m, n, NULL, NULL, NULL, NULL, 0, C, d, 3, 1000, opts, info, NULL, NULL, NULL); // lin. ineq. constraints, analytic Jacobian //ret=dlevmar_bleic_dif(mod2hs52, p, x, m, n, NULL, NULL, NULL, NULL, 0, C, d, 3, 1000, opts, info, NULL, NULL, NULL); // lin. ineq. constraints, no Jacobian } break; case 20: /* Hock - Schittkowski modified problem 76 */ m=4; n=4; p[0]=0.5; p[1]=0.5; p[2]=0.5; p[3]=0.5; for(i=0; i<n; i++) x[i]=0.0; { double A[1*4]={0.0, 1.0, 4.0, 0.0}, b[1]={1.5}; double C[2*4]={-1.0, -2.0, -1.0, -1.0, -3.0, -1.0, -2.0, 1.0}, d[2]={-5.0, -0.4}; double lb[4]={0.0, 0.0, 0.0, 0.0}; ret=dlevmar_bleic_der(modhs76, jacmodhs76, p, x, m, n, lb, NULL, A, b, 1, C, d, 2, 1000, opts, info, NULL, NULL, NULL); // lin. ineq. constraints, analytic Jacobian //ret=dlevmar_bleic_dif(modhs76, p, x, m, n, lb, NULL, A, b, 1, C, d, 2, 1000, opts, info, NULL, NULL, NULL); // lin. ineq. constraints, no Jacobian /* variations: * if no lb is used, the minimizer is (-0.1135922 0.1330097 0.3417476 0.07572816) * if the rhs of constr2 is 4.0, the minimizer is (0.0, 0.166667, 0.333333, 0.0) */ } break; #endif /* HAVE_LAPACK */ } /* switch */ printf("Results for %s:\n", probname[problem]); printf("Levenberg-Marquardt returned %d in %g iter, reason %g\nSolution: ", ret, info[5], info[6]); for(i=0; i<m; ++i) printf("%.7g ", p[i]); printf("\n\nMinimization info:\n"); for(i=0; i<LM_INFO_SZ; ++i) printf("%g ", info[i]); printf("\n"); return 0; }
static PyObject * _pylm_dlevmar_generic(PyObject *mod, PyObject *args, PyObject *kwds, char *argstring, char *kwlist[], int jacobian, int bounds) { PyObject *func = NULL; PyObject *jacf = NULL; PyObject *initial = NULL, *initial_npy = NULL; PyObject *measurements = NULL, *measurements_npy = NULL; PyObject *lower = NULL, *lower_npy = NULL; PyObject *upper = NULL, *upper_npy = NULL; PyObject *opts = NULL, *opts_npy = NULL; PyObject *covar = NULL; PyObject *retval = NULL; PyObject *info = NULL; pylm_callback_data *pydata = NULL; double *c_initial = NULL; double *c_measurements = NULL; double *c_opts = NULL; double *c_lower = NULL; double *c_upper = NULL; double *c_covar = NULL; int max_iter = 0; int run_iter = 0; int m = 0, n = 0; double c_info[LM_INFO_SZ]; int nopts; // If finite-difference approximate Jacobians are used, we // need 5 optional params; otherwise 4. if (jacobian){ nopts = 4; } else { nopts = 5; } // parse arguments if (!bounds) { if (jacobian) { if (!PyArg_ParseTupleAndKeywords(args, kwds, argstring, kwlist, &func, &jacf, &initial, &measurements, &max_iter, &opts, &covar)){ return NULL; } } else { if (!PyArg_ParseTupleAndKeywords(args, kwds, argstring, kwlist, &func, &initial, &measurements, &max_iter, &opts, &covar)){ return NULL; } } } else { if (jacobian) { if (!PyArg_ParseTupleAndKeywords(args, kwds, argstring, kwlist, &func, &jacf, &initial, &measurements, &lower, &upper, &max_iter, &opts, &covar)){ return NULL; } } else { if (!PyArg_ParseTupleAndKeywords(args, kwds, argstring, kwlist, &func, &initial, &measurements, &lower, &upper, &max_iter, &opts, &covar)){ return NULL; } } } // Check each variable type if (!PyCallable_Check(func)) { PyErr_SetString(PyExc_TypeError, "func must be a callable object"); return NULL; } if (!PyArray_Check(initial)) { PyErr_SetString(PyExc_TypeError, "initial must be a numpy array"); return NULL; } if (!PyArray_Check(measurements)) { PyErr_SetString(PyExc_TypeError, "measurements must be a numpy array"); return NULL; } if (jacobian && !PyCallable_Check(jacf)) { PyErr_SetString(PyExc_TypeError, "jacf must be a callable object"); return NULL; } if (lower && !PyArray_Check(lower)) { PyErr_SetString(PyExc_TypeError, "lower bounds must be a numpy array"); return NULL; } if (upper && !PyArray_Check(upper)) { PyErr_SetString(PyExc_TypeError, "upper bounds must be a numpy array"); return NULL; } if (opts && !PyArray_Check(opts) && (PyArray_Size(opts) != nopts)) { if (nopts == 4){ PyErr_SetString(PyExc_TypeError, "opts must be a numpy vector of length 4."); } else { PyErr_SetString(PyExc_TypeError, "opts must be a numpy vector of length 5."); } return NULL; } // convert python types into C pydata = PyMem_Malloc(sizeof(pydata)); if(!pydata){ PyErr_SetString(PyExc_RuntimeError, "Error in allocating memory for data."); return NULL; } pydata->func = func; pydata->jacf = jacf; initial_npy = PyArray_FROMANY(initial, NPY_DOUBLE, 0, 0, NPY_INOUT_ARRAY); measurements_npy = PyArray_FROMANY(measurements, NPY_DOUBLE, 0, 0, NPY_IN_ARRAY); if(!initial_npy || !measurements_npy){ // Cannot create array PyErr_SetString(PyExc_RuntimeError, "Error in creating arrays from input data."); //Py_XDECREF(initial_npy); //Py_XDECREF(measurements_npy); return NULL; } c_initial = (double *)PyArray_DATA(initial_npy); c_measurements = (double *)PyArray_DATA(measurements_npy); m = PyArray_SIZE(initial_npy); n = PyArray_SIZE(measurements_npy); npy_intp dims[2] = {m, m}; covar = PyArray_SimpleNew(2, dims, NPY_DOUBLE); c_covar = PyArray_DATA(covar); if (lower){ lower_npy = PyArray_FROMANY(lower, PyArray_DOUBLE, 0, 0, NPY_IN_ARRAY); c_lower = PyArray_DATA(lower_npy); // TODO check dims } if (upper){ upper_npy = PyArray_FROMANY(upper, PyArray_DOUBLE, 0, 0, NPY_IN_ARRAY); c_upper = PyArray_DATA(upper_npy); // TODO check dims } if (opts) { opts_npy = PyArray_FROMANY(opts, PyArray_DOUBLE, 0, 0, NPY_IN_ARRAY); c_opts = PyArray_DATA(opts_npy); // TODO check dims } // call function to do the fitting if (!bounds) { if (jacobian) { run_iter = dlevmar_der(_pylm_func_callback, _pylm_jacf_callback, c_initial, c_measurements, m, n, max_iter, c_opts, c_info, NULL, c_covar, pydata); } else { run_iter = dlevmar_dif(_pylm_func_callback, c_initial, c_measurements, m, n, max_iter, c_opts, c_info, NULL, c_covar, pydata); } } else { if (jacobian) { run_iter = dlevmar_bc_der(_pylm_func_callback, _pylm_jacf_callback, c_initial, c_measurements, m, n, c_lower, c_upper, max_iter, c_opts, c_info, NULL, c_covar, pydata); } else { run_iter = dlevmar_bc_dif(_pylm_func_callback, c_initial, c_measurements, m, n, c_lower, c_upper, max_iter, c_opts, c_info, NULL, c_covar, pydata); } } // convert results back into python if (run_iter > 0) { npy_intp dims[1] = {m}; retval = PyArray_SimpleNewFromData(1, dims, PyArray_DOUBLE, c_initial); } else { retval = Py_None; Py_INCREF(Py_None); } if (pydata) { PyMem_Free(pydata); } // convert additional information into python info = Py_BuildValue("{s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d,s:d}", "initial_e2", c_info[0], "estimate_e2", c_info[1], "estimate_Jt", c_info[2], "estimate_Dp2", c_info[3], "estimate_mu", c_info[4], "iterations", c_info[5], "termination", c_info[6], "function_evaluations", c_info[7], "jacobian_evaluations", c_info[8]); //Py_XDECREF(measurements_npy); //Py_XDECREF(initial_npy); //Py_XDECREF(lower_npy); //Py_XDECREF(upper_npy); //Py_XDECREF(opts_npy); return Py_BuildValue("(OOiO)", retval, covar, run_iter, info, NULL); }
bool SQ_fitter_b<PointT>::minimize( const int &_type, const PointCloudPtr &_cloud, const SQ_parameters &_in, SQ_parameters &_out, double &_error ) { // Parameters initially _in: _out = _in; // Set necessary parameters int n = _cloud->points.size(); int m = 13; double p[m]; // Parameters of SQ double y[n]; // Values we want to achieve double opts[LM_OPTS_SZ]; double info[LM_INFO_SZ]; opts[0] = LM_INIT_MU; opts[1] = 1E-15; opts[2] = 1E-15; opts[3] = 1E-20; opts[4] = LM_DIFF_DELTA; struct levmar_data data; data.x = new double[n]; data.y = new double[n]; data.z = new double[n]; data.num = n; int i; int ret; typename pcl::PointCloud<PointT>::iterator pit; for( pit = _cloud->begin(), i = 0; pit != _cloud->end(); ++pit, ++i ) { data.x[i] = (*pit).x; data.y[i] = (*pit).y; data.z[i] = (*pit).z; } // Set minimizer value to zero (could be 1, depending of what equation you are minimizing) for( i = 0; i < n; ++i ) { y[i] = 0.0; } // Initialize values for parameters p for( i = 0; i < 3; ++i ) { p[i] = _in.dim[i]; } for( i = 0; i < 2; ++i ) { p[i+3] = _in.e[i]; } for( i = 0; i < 3; ++i ) { p[i+5] = _in.trans[i]; } for( i = 0; i < 3; ++i ) { p[i+8] = _in.rot[i]; } p[11] = _in.alpha; p[12] = _in.k; printf("Initial p: %f %f %f %f %f %f %f %f %f %f %f alpha: %f k: %f \n", p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12] ); // Set limits double ub[m], lb[m]; for( i = 0; i < 3; ++i ) { lb[i] = this->mLowerLim_dim[i]; ub[i] = this->mUpperLim_dim[i]; } for( i = 0; i < 2; ++i ) { lb[i+3] = this->mLowerLim_e; ub[i+3] = this->mUpperLim_e; } for( i = 0; i < 3; ++i ) { lb[i+5] = this->mLowerLim_trans[i]; ub[i+5] = this->mUpperLim_trans[i]; } for( i = 0; i < 3; ++i ) { lb[i+8] = this->mLowerLim_rot[i]; ub[i+8] = this->mUpperLim_rot[i]; } lb[11] = mLowerLim_alpha; ub[11] = mUpperLim_alpha; lb[12] = mLowerLim_k; ub[12] = mUpperLim_k; switch( _type ) { case SQ_FX_RADIAL: { ret = dlevmar_bc_der( fr_add_b, Jr_add_b, p, y, m, n, lb, ub, NULL, 5000, opts, info, NULL, NULL, (void*)&data ); } break; case SQ_FX_ICHIM: { ret = dlevmar_bc_der( fi_add_b, Ji_add_b, p, y, m, n, lb, ub, NULL, 1000, opts, info, NULL, NULL, (void*)&data ); } break; case SQ_FX_SOLINA: { ret = dlevmar_bc_der( fs_add_b, Js_add_b, p, y, m, n, lb, ub, NULL, 1000, opts, info, NULL, NULL, (void*)&data ); } break; case SQ_FX_CHEVALIER: { ret = dlevmar_bc_der( fc_add_b, Jc_add_b, p, y, m, n, lb, ub, NULL, 1000, opts, info, NULL, NULL, (void*)&data ); } break; case SQ_FX_5: { ret = dlevmar_bc_der( f5_add_b, J5_add_b, p, y, m, n, lb, ub, NULL, 1000, opts, info, NULL, NULL, (void*)&data ); } break; case SQ_FX_6: { ret = dlevmar_bc_der( f6_add_b, J6_add_b, p, y, m, n, lb, ub, NULL, 1000, opts, info, NULL, NULL, (void*)&data ); } break; } // end switch // Fill _out for( i = 0; i < 3; ++i ) { _out.dim[i] = p[i]; } for( i = 0; i < 2; ++i ) { _out.e[i] = p[i+3]; } for( i = 0; i < 3; ++i ) { _out.trans[i] = p[i+5]; } for( i = 0; i < 3; ++i ) { _out.rot[i] = p[i+8]; } _out.alpha = p[11]; _out.k = p[12]; _out.type = BENT; // Return status and error double eg, er; get_error( _out, this->cloud_, eg, er, _error ); // If stopped by invalid (TODO: Add other reasons) if( info[6] == 7 ) { return false; } else { return true; } }
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *Prhs[]) { register int i; register double *pdbl; mxArray **prhs=(mxArray **)&Prhs[0], *At, *Ct; struct mexdata mdata; int len, status; double *p, *p0, *ret, *x; int m, n, havejac, Arows, Crows, itmax, nopts, mintype, nextra; double opts[LM_OPTS_SZ]={LM_INIT_MU, LM_STOP_THRESH, LM_STOP_THRESH, LM_STOP_THRESH, LM_DIFF_DELTA}; double info[LM_INFO_SZ]; double *lb=NULL, *ub=NULL, *A=NULL, *b=NULL, *wghts=NULL, *C=NULL, *d=NULL, *covar=NULL; /* parse input args; start by checking their number */ if((nrhs<5)) matlabFmtdErrMsgTxt("levmar: at least 5 input arguments required (got %d).", nrhs); if(nlhs>4) matlabFmtdErrMsgTxt("levmar: too many output arguments (max. 4, got %d).", nlhs); else if(nlhs<2) matlabFmtdErrMsgTxt("levmar: too few output arguments (min. 2, got %d).", nlhs); /* note that in order to accommodate optional args, prhs & nrhs are adjusted accordingly below */ /** func **/ /* first argument must be a string , i.e. a char row vector */ if(mxIsChar(prhs[0])!=1) mexErrMsgTxt("levmar: first argument must be a string."); if(mxGetM(prhs[0])!=1) mexErrMsgTxt("levmar: first argument must be a string (i.e. char row vector)."); /* store supplied name */ len=mxGetN(prhs[0])+1; mdata.fname=mxCalloc(len, sizeof(char)); status=mxGetString(prhs[0], mdata.fname, len); if(status!=0) mexErrMsgTxt("levmar: not enough space. String is truncated."); /** jac (optional) **/ /* check whether second argument is a string */ if(mxIsChar(prhs[1])==1){ if(mxGetM(prhs[1])!=1) mexErrMsgTxt("levmar: second argument must be a string (i.e. row vector)."); /* store supplied name */ len=mxGetN(prhs[1])+1; mdata.jacname=mxCalloc(len, sizeof(char)); status=mxGetString(prhs[1], mdata.jacname, len); if(status!=0) mexErrMsgTxt("levmar: not enough space. String is truncated."); havejac=1; ++prhs; --nrhs; } else{ mdata.jacname=NULL; havejac=0; } #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: %s analytic Jacobian\n", havejac? "with" : "no"); #endif /* DEBUG */ /* CHECK if(!mxIsDouble(prhs[1]) || mxIsComplex(prhs[1]) || !(mxGetM(prhs[1])==1 && mxGetN(prhs[1])==1)) */ /** p0 **/ /* the second required argument must be a real row or column vector */ if(!mxIsDouble(prhs[1]) || mxIsComplex(prhs[1]) || !(mxGetM(prhs[1])==1 || mxGetN(prhs[1])==1)) mexErrMsgTxt("levmar: p0 must be a real vector."); p0=mxGetPr(prhs[1]); /* determine if we have a row or column vector and retrieve its * size, i.e. the number of parameters */ if(mxGetM(prhs[1])==1){ m=mxGetN(prhs[1]); mdata.isrow_p0=1; } else{ m=mxGetM(prhs[1]); mdata.isrow_p0=0; } /* copy input parameter vector to avoid destroying it */ p=mxMalloc(m*sizeof(double)); for(i=0; i<m; ++i) p[i]=p0[i]; /** x **/ /* the third required argument must be a real row or column vector */ if(!mxIsDouble(prhs[2]) || mxIsComplex(prhs[2]) || !(mxGetM(prhs[2])==1 || mxGetN(prhs[2])==1)) mexErrMsgTxt("levmar: x must be a real vector."); x=mxGetPr(prhs[2]); n=__MAX__(mxGetM(prhs[2]), mxGetN(prhs[2])); /** itmax **/ /* the fourth required argument must be a scalar */ if(!mxIsDouble(prhs[3]) || mxIsComplex(prhs[3]) || mxGetM(prhs[3])!=1 || mxGetN(prhs[3])!=1) mexErrMsgTxt("levmar: itmax must be a scalar."); itmax=(int)mxGetScalar(prhs[3]); /** opts **/ /* the fifth required argument must be a real row or column vector */ if(!mxIsDouble(prhs[4]) || mxIsComplex(prhs[4]) || (!(mxGetM(prhs[4])==1 || mxGetN(prhs[4])==1) && !(mxGetM(prhs[4])==0 && mxGetN(prhs[4])==0))) mexErrMsgTxt("levmar: opts must be a real vector."); pdbl=mxGetPr(prhs[4]); nopts=__MAX__(mxGetM(prhs[4]), mxGetN(prhs[4])); if(nopts!=0){ /* if opts==[], nothing needs to be done and the defaults are used */ if(nopts>LM_OPTS_SZ) matlabFmtdErrMsgTxt("levmar: opts must have at most %d elements, got %d.", LM_OPTS_SZ, nopts); else if(nopts<((havejac)? LM_OPTS_SZ-1 : LM_OPTS_SZ)) matlabFmtdWarnMsgTxt("levmar: only the %d first elements of opts specified, remaining set to defaults.", nopts); for(i=0; i<nopts; ++i) opts[i]=pdbl[i]; } #ifdef DEBUG else{ fflush(stderr); fprintf(stderr, "LEVMAR: empty options vector, using defaults\n"); } #endif /* DEBUG */ /** mintype (optional) **/ /* check whether sixth argument is a string */ if(nrhs>=6 && mxIsChar(prhs[5])==1 && mxGetM(prhs[5])==1){ char *minhowto; /* examine supplied name */ len=mxGetN(prhs[5])+1; minhowto=mxCalloc(len, sizeof(char)); status=mxGetString(prhs[5], minhowto, len); if(status!=0) mexErrMsgTxt("levmar: not enough space. String is truncated."); for(i=0; minhowto[i]; ++i) minhowto[i]=tolower(minhowto[i]); if(!strncmp(minhowto, "unc", 3)) mintype=MIN_UNCONSTRAINED; else if(!strncmp(minhowto, "bc", 2)) mintype=MIN_CONSTRAINED_BC; else if(!strncmp(minhowto, "lec", 3)) mintype=MIN_CONSTRAINED_LEC; else if(!strncmp(minhowto, "blec", 4)) mintype=MIN_CONSTRAINED_BLEC; else if(!strncmp(minhowto, "bleic", 5)) mintype=MIN_CONSTRAINED_BLEIC; else if(!strncmp(minhowto, "blic", 4)) mintype=MIN_CONSTRAINED_BLIC; else if(!strncmp(minhowto, "leic", 4)) mintype=MIN_CONSTRAINED_LEIC; else if(!strncmp(minhowto, "lic", 3)) mintype=MIN_CONSTRAINED_BLIC; else matlabFmtdErrMsgTxt("levmar: unknown minimization type '%s'.", minhowto); mxFree(minhowto); ++prhs; --nrhs; } else mintype=MIN_UNCONSTRAINED; if(mintype==MIN_UNCONSTRAINED) goto extraargs; /* arguments below this point are optional and their presence depends * upon the minimization type determined above */ /** lb, ub **/ if(nrhs>=7 && (mintype==MIN_CONSTRAINED_BC || mintype==MIN_CONSTRAINED_BLEC || mintype==MIN_CONSTRAINED_BLIC || mintype==MIN_CONSTRAINED_BLEIC)){ /* check if the next two arguments are real row or column vectors */ if(mxIsDouble(prhs[5]) && !mxIsComplex(prhs[5]) && (mxGetM(prhs[5])==1 || mxGetN(prhs[5])==1)){ if(mxIsDouble(prhs[6]) && !mxIsComplex(prhs[6]) && (mxGetM(prhs[6])==1 || mxGetN(prhs[6])==1)){ if((i=__MAX__(mxGetM(prhs[5]), mxGetN(prhs[5])))!=m) matlabFmtdErrMsgTxt("levmar: lb must have %d elements, got %d.", m, i); if((i=__MAX__(mxGetM(prhs[6]), mxGetN(prhs[6])))!=m) matlabFmtdErrMsgTxt("levmar: ub must have %d elements, got %d.", m, i); lb=mxGetPr(prhs[5]); ub=mxGetPr(prhs[6]); prhs+=2; nrhs-=2; } } } /** A, b **/ if(nrhs>=7 && (mintype==MIN_CONSTRAINED_LEC || mintype==MIN_CONSTRAINED_BLEC || mintype==MIN_CONSTRAINED_LEIC || mintype==MIN_CONSTRAINED_BLEIC)){ /* check if the next two arguments are a real matrix and a real row or column vector */ if(mxIsDouble(prhs[5]) && !mxIsComplex(prhs[5]) && mxGetM(prhs[5])>=1 && mxGetN(prhs[5])>=1){ if(mxIsDouble(prhs[6]) && !mxIsComplex(prhs[6]) && (mxGetM(prhs[6])==1 || mxGetN(prhs[6])==1)){ if((i=mxGetN(prhs[5]))!=m) matlabFmtdErrMsgTxt("levmar: A must have %d columns, got %d.", m, i); if((i=__MAX__(mxGetM(prhs[6]), mxGetN(prhs[6])))!=(Arows=mxGetM(prhs[5]))) matlabFmtdErrMsgTxt("levmar: b must have %d elements, got %d.", Arows, i); At=prhs[5]; b=mxGetPr(prhs[6]); A=getTranspose(At); prhs+=2; nrhs-=2; } } } /* wghts */ /* check if we have a weights vector */ if(nrhs>=6 && mintype==MIN_CONSTRAINED_BLEC){ /* only check if we have seen both box & linear constraints */ if(mxIsDouble(prhs[5]) && !mxIsComplex(prhs[5]) && (mxGetM(prhs[5])==1 || mxGetN(prhs[5])==1)){ if(__MAX__(mxGetM(prhs[5]), mxGetN(prhs[5]))==m){ wghts=mxGetPr(prhs[5]); ++prhs; --nrhs; } } } /** C, d **/ if(nrhs>=7 && (mintype==MIN_CONSTRAINED_BLEIC || mintype==MIN_CONSTRAINED_BLIC || mintype==MIN_CONSTRAINED_LEIC || mintype==MIN_CONSTRAINED_LIC)){ /* check if the next two arguments are a real matrix and a real row or column vector */ if(mxIsDouble(prhs[5]) && !mxIsComplex(prhs[5]) && mxGetM(prhs[5])>=1 && mxGetN(prhs[5])>=1){ if(mxIsDouble(prhs[6]) && !mxIsComplex(prhs[6]) && (mxGetM(prhs[6])==1 || mxGetN(prhs[6])==1)){ if((i=mxGetN(prhs[5]))!=m) matlabFmtdErrMsgTxt("levmar: C must have %d columns, got %d.", m, i); if((i=__MAX__(mxGetM(prhs[6]), mxGetN(prhs[6])))!=(Crows=mxGetM(prhs[5]))) matlabFmtdErrMsgTxt("levmar: d must have %d elements, got %d.", Crows, i); Ct=prhs[5]; d=mxGetPr(prhs[6]); C=getTranspose(Ct); prhs+=2; nrhs-=2; } } } /* arguments below this point are assumed to be extra arguments passed * to every invocation of the fitting function and its Jacobian */ extraargs: /* handle any extra args and allocate memory for * passing the current parameter estimate to matlab */ nextra=nrhs-5; mdata.nrhs=nextra+1; mdata.rhs=(mxArray **)mxMalloc(mdata.nrhs*sizeof(mxArray *)); for(i=0; i<nextra; ++i) mdata.rhs[i+1]=(mxArray *)prhs[nrhs-nextra+i]; /* discard 'const' modifier */ #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: %d extra args\n", nextra); #endif /* DEBUG */ if(mdata.isrow_p0){ /* row vector */ mdata.rhs[0]=mxCreateDoubleMatrix(1, m, mxREAL); /* mxSetM(mdata.rhs[0], 1); mxSetN(mdata.rhs[0], m); */ } else{ /* column vector */ mdata.rhs[0]=mxCreateDoubleMatrix(m, 1, mxREAL); /* mxSetM(mdata.rhs[0], m); mxSetN(mdata.rhs[0], 1); */ } /* ensure that the supplied function & Jacobian are as expected */ if(checkFuncAndJacobian(p, m, n, havejac, &mdata)){ status=LM_ERROR; goto cleanup; } if(nlhs>3) /* covariance output required */ covar=mxMalloc(m*m*sizeof(double)); /* invoke levmar */ switch(mintype){ case MIN_UNCONSTRAINED: /* no constraints */ if(havejac) status=dlevmar_der(func, jacfunc, p, x, m, n, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_dif(func, p, x, m, n, itmax, opts, info, NULL, covar, (void *)&mdata); #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_der()/dlevmar_dif()\n"); #endif /* DEBUG */ break; case MIN_CONSTRAINED_BC: /* box constraints */ if(havejac) status=dlevmar_bc_der(func, jacfunc, p, x, m, n, lb, ub, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_bc_dif(func, p, x, m, n, lb, ub, itmax, opts, info, NULL, covar, (void *)&mdata); #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_bc_der()/dlevmar_bc_dif()\n"); #endif /* DEBUG */ break; case MIN_CONSTRAINED_LEC: /* linear equation constraints */ #ifdef HAVE_LAPACK if(havejac) status=dlevmar_lec_der(func, jacfunc, p, x, m, n, A, b, Arows, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_lec_dif(func, p, x, m, n, A, b, Arows, itmax, opts, info, NULL, covar, (void *)&mdata); #else mexErrMsgTxt("levmar: no linear constraints support, HAVE_LAPACK was not defined during MEX-file compilation."); #endif /* HAVE_LAPACK */ #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_lec_der()/dlevmar_lec_dif()\n"); #endif /* DEBUG */ break; case MIN_CONSTRAINED_BLEC: /* box & linear equation constraints */ #ifdef HAVE_LAPACK if(havejac) status=dlevmar_blec_der(func, jacfunc, p, x, m, n, lb, ub, A, b, Arows, wghts, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_blec_dif(func, p, x, m, n, lb, ub, A, b, Arows, wghts, itmax, opts, info, NULL, covar, (void *)&mdata); #else mexErrMsgTxt("levmar: no box & linear constraints support, HAVE_LAPACK was not defined during MEX-file compilation."); #endif /* HAVE_LAPACK */ #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_blec_der()/dlevmar_blec_dif()\n"); #endif /* DEBUG */ break; case MIN_CONSTRAINED_BLEIC: /* box, linear equation & inequalities constraints */ #ifdef HAVE_LAPACK if(havejac) status=dlevmar_bleic_der(func, jacfunc, p, x, m, n, lb, ub, A, b, Arows, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_bleic_dif(func, p, x, m, n, lb, ub, A, b, Arows, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); #else mexErrMsgTxt("levmar: no box, linear equation & inequality constraints support, HAVE_LAPACK was not defined during MEX-file compilation."); #endif /* HAVE_LAPACK */ #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_bleic_der()/dlevmar_bleic_dif()\n"); #endif /* DEBUG */ break; case MIN_CONSTRAINED_BLIC: /* box, linear inequalities constraints */ #ifdef HAVE_LAPACK if(havejac) status=dlevmar_bleic_der(func, jacfunc, p, x, m, n, lb, ub, NULL, NULL, 0, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_bleic_dif(func, p, x, m, n, lb, ub, NULL, NULL, 0, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); #else mexErrMsgTxt("levmar: no box & linear inequality constraints support, HAVE_LAPACK was not defined during MEX-file compilation."); #endif /* HAVE_LAPACK */ #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_blic_der()/dlevmar_blic_dif()\n"); #endif /* DEBUG */ break; case MIN_CONSTRAINED_LEIC: /* linear equation & inequalities constraints */ #ifdef HAVE_LAPACK if(havejac) status=dlevmar_bleic_der(func, jacfunc, p, x, m, n, NULL, NULL, A, b, Arows, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_bleic_dif(func, p, x, m, n, NULL, NULL, A, b, Arows, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); #else mexErrMsgTxt("levmar: no linear equation & inequality constraints support, HAVE_LAPACK was not defined during MEX-file compilation."); #endif /* HAVE_LAPACK */ #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_leic_der()/dlevmar_leic_dif()\n"); #endif /* DEBUG */ break; case MIN_CONSTRAINED_LIC: /* linear inequalities constraints */ #ifdef HAVE_LAPACK if(havejac) status=dlevmar_bleic_der(func, jacfunc, p, x, m, n, NULL, NULL, NULL, NULL, 0, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); else status=dlevmar_bleic_dif(func, p, x, m, n, NULL, NULL, NULL, NULL, 0, C, d, Crows, itmax, opts, info, NULL, covar, (void *)&mdata); #else mexErrMsgTxt("levmar: no linear equation & inequality constraints support, HAVE_LAPACK was not defined during MEX-file compilation."); #endif /* HAVE_LAPACK */ #ifdef DEBUG fflush(stderr); fprintf(stderr, "LEVMAR: calling dlevmar_lic_der()/dlevmar_lic_dif()\n"); #endif /* DEBUG */ break; default: mexErrMsgTxt("levmar: unexpected internal error."); } #ifdef DEBUG fflush(stderr); printf("LEVMAR: minimization returned %d in %g iter, reason %g\n\tSolution: ", status, info[5], info[6]); for(i=0; i<m; ++i) printf("%.7g ", p[i]); printf("\n\n\tMinimization info:\n\t"); for(i=0; i<LM_INFO_SZ; ++i) printf("%g ", info[i]); printf("\n"); #endif /* DEBUG */ /* copy back return results */ /** ret **/ plhs[0]=mxCreateDoubleMatrix(1, 1, mxREAL); ret=mxGetPr(plhs[0]); ret[0]=(double)status; /** popt **/ plhs[1]=(mdata.isrow_p0==1)? mxCreateDoubleMatrix(1, m, mxREAL) : mxCreateDoubleMatrix(m, 1, mxREAL); pdbl=mxGetPr(plhs[1]); for(i=0; i<m; ++i) pdbl[i]=p[i]; /** info **/ if(nlhs>2){ plhs[2]=mxCreateDoubleMatrix(1, LM_INFO_SZ, mxREAL); pdbl=mxGetPr(plhs[2]); for(i=0; i<LM_INFO_SZ; ++i) pdbl[i]=info[i]; } /** covar **/ if(nlhs>3){ plhs[3]=mxCreateDoubleMatrix(m, m, mxREAL); pdbl=mxGetPr(plhs[3]); for(i=0; i<m*m; ++i) /* covariance matrices are symmetric, thus no need to transpose! */ pdbl[i]=covar[i]; } cleanup: /* cleanup */ mxDestroyArray(mdata.rhs[0]); if(A) mxFree(A); if(C) mxFree(C); mxFree(mdata.fname); if(havejac) mxFree(mdata.jacname); mxFree(p); mxFree(mdata.rhs); if(covar) mxFree(covar); if(status==LM_ERROR) mexWarnMsgTxt("levmar: optimization returned with an error!"); }
double stfnum::lmFit( const Vector_double& data, double dt, const stfnum::storedFunc& fitFunc, const Vector_double& opts, bool use_scaling, Vector_double& p, std::string& info, int& warning ) { // Basic range checking: if (fitFunc.pInfo.size()!=p.size()) { std::string msg("Error in stfnum::lmFit()\n" "function parameters (p_fit) and parameters entered (p) have different sizes"); throw std::runtime_error(msg); } if ( opts.size() != 6 ) { std::string msg("Error in stfnum::lmFit()\n" "wrong number of options"); throw std::runtime_error(msg); } bool constrained = false; std::vector< double > constrains_lm_lb( fitFunc.pInfo.size() ); std::vector< double > constrains_lm_ub( fitFunc.pInfo.size() ); bool can_scale = use_scaling; for ( unsigned n_p=0; n_p < fitFunc.pInfo.size(); ++n_p ) { if ( fitFunc.pInfo[n_p].constrained ) { constrained = true; constrains_lm_lb[n_p] = fitFunc.pInfo[n_p].constr_lb; constrains_lm_ub[n_p] = fitFunc.pInfo[n_p].constr_ub; } else { constrains_lm_lb[n_p] = -DBL_MAX; constrains_lm_ub[n_p] = DBL_MAX; } if ( can_scale ) { if (fitFunc.pInfo[n_p].scale == stfnum::noscale) { can_scale = false; } } } // Store the functions at global scope: saveFunc(fitFunc.func); saveJac(fitFunc.jac); double info_id[LM_INFO_SZ]; Vector_double data_ptr(data); Vector_double xyscale(4); if (can_scale) { xyscale = get_scale(data_ptr, dt); } // The parameters need to be separated into two parts: // Those that are to be fitted and those that the client wants // to keep constant. Since there is no native support to // do so in Lourakis' routines, the workaround is a little // tricky, making (ab)use of the *void pointer: // number of parameters that need to be fitted: int n_fitted=0; for ( unsigned n_p=0; n_p < fitFunc.pInfo.size(); ++n_p ) { n_fitted += fitFunc.pInfo[n_p].toFit; } // parameters that need to be fitted: Vector_double p_toFit(n_fitted); std::deque<bool> p_fit_bool( fitFunc.pInfo.size() ); // parameters that are held constant: Vector_double p_const( fitFunc.pInfo.size()-n_fitted ); for ( unsigned n_p=0, n_c=0, n_f=0; n_p < fitFunc.pInfo.size(); ++n_p ) { if (fitFunc.pInfo[n_p].toFit) { p_toFit[n_f++] = p[n_p]; if (can_scale) { p_toFit[n_f-1] = fitFunc.pInfo[n_p].scale(p_toFit[n_f-1], xyscale[0], xyscale[1], xyscale[2], xyscale[3]); } } else { p_const[n_c++] = p[n_p]; if (can_scale) { p_const[n_c-1] = fitFunc.pInfo[n_p].scale(p_const[n_c-1], xyscale[0], xyscale[1], xyscale[2], xyscale[3]); } } p_fit_bool[n_p] = fitFunc.pInfo[n_p].toFit; } // size * dt_new = 1 -> dt_new = 1.0/size double dt_finfo = dt; if (can_scale) dt_finfo = 1.0/data_ptr.size(); fitInfo fInfo( p_fit_bool, p_const, dt_finfo ); // make l-value of opts: Vector_double opts_l(5); for (std::size_t n=0; n < 4; ++n) opts_l[n] = opts[n]; opts_l[4] = -1e-6; int it = 0; if (p_toFit.size()!=0 && data_ptr.size()!=0) { double old_info_id[LM_INFO_SZ]; // initialize with initial parameter guess: Vector_double old_p_toFit(p_toFit); #ifdef _DEBUG std::ostringstream optsMsg; optsMsg << "\nopts: "; for (std::size_t n_p=0; n_p < opts.size(); ++n_p) optsMsg << opts[n_p] << "\t"; optsMsg << "\n" << "data_ptr[" << data_ptr.size()-1 << "]=" << data_ptr[data_ptr.size()-1] << "\n"; optsMsg << "constrains_lm_lb: "; for (std::size_t n_p=0; n_p < constrains_lm_lb.size(); ++n_p) optsMsg << constrains_lm_lb[n_p] << "\t"; optsMsg << "\n" << "constrains_lm_ub: "; for (std::size_t n_p=0; n_p < constrains_lm_ub.size(); ++n_p) optsMsg << constrains_lm_ub[n_p] << "\t"; optsMsg << "\n\n"; std::cout << optsMsg; #endif while ( 1 ) { #ifdef _DEBUG std::ostringstream paramMsg; paramMsg << "Pass: "******"\t"; paramMsg << "p_toFit: "; for (std::size_t n_p=0; n_p < p_toFit.size(); ++n_p) paramMsg << p_toFit[n_p] << "\t"; paramMsg << "\n"; std::cout << paramMsg.str().c_str(); #endif if ( !fitFunc.hasJac ) { if ( !constrained ) { dlevmar_dif( c_func_lour, &p_toFit[0], &data_ptr[0], n_fitted, (int)data.size(), (int)opts[4], &opts_l[0], info_id, NULL, NULL, &fInfo ); } else { dlevmar_bc_dif( c_func_lour, &p_toFit[0], &data_ptr[0], n_fitted, (int)data.size(), &constrains_lm_lb[0], &constrains_lm_ub[0], NULL, (int)opts[4], &opts_l[0], info_id, NULL, NULL, &fInfo ); } } else { if ( !constrained ) { dlevmar_der( c_func_lour, c_jac_lour, &p_toFit[0], &data_ptr[0], n_fitted, (int)data.size(), (int)opts[4], &opts_l[0], info_id, NULL, NULL, &fInfo ); } else { dlevmar_bc_der( c_func_lour, c_jac_lour, &p_toFit[0], &data_ptr[0], n_fitted, (int)data.size(), &constrains_lm_lb[0], &constrains_lm_ub[0], NULL, (int)opts[4], &opts_l[0], info_id, NULL, NULL, &fInfo ); } } it++; if ( info_id[1] != info_id[1] ) { // restore previous parameters if new chisqr is NaN: p_toFit = old_p_toFit; } else { double dchisqr = (info_id[0] - info_id[1]) / info_id[1]; // (old chisqr - new chisqr) / new_chisqr if ( dchisqr < 0 ) { // restore previous results and exit if new chisqr is larger: for ( int n_i = 0; n_i < LM_INFO_SZ; ++n_i ) info_id[n_i] = old_info_id[n_i]; p_toFit = old_p_toFit; break; } if ( dchisqr < 1e-5 ) { // Keep current results and exit if change in chisqr is below threshold break; } // otherwise, store results and continue iterating: for ( int n_i = 0; n_i < LM_INFO_SZ; ++n_i ) old_info_id[n_i] = info_id[n_i]; old_p_toFit = p_toFit; } if ( it >= opts[5] ) // Exit if maximal number of iterations is reached break; // decrease initial step size for next iteration: opts_l[0] *= 1e-4; } } else { std::runtime_error e("Array of size zero in lmFit"); throw e; } // copy back the fitted parameters to p: for ( unsigned n_p=0, n_f=0, n_c=0; n_p<fitFunc.pInfo.size(); ++n_p ) { if (fitFunc.pInfo[n_p].toFit) { p[n_p] = p_toFit[n_f++]; } else { p[n_p] = p_const[n_c++]; } if (can_scale) { p[n_p] = fitFunc.pInfo[n_p].unscale(p[n_p], xyscale[0], xyscale[1], xyscale[2], xyscale[3]); } } std::ostringstream str_info; str_info << "Passes: " << it; str_info << "\nIterations during last pass: "******"\nStopping reason during last pass:"******"\nStopped by small gradient of squared error."; warning = 0; break; case 2: str_info << "\nStopped by small rel. parameter change."; warning = 0; break; case 3: str_info << "\nReached max. number of iterations. Restart\n" << "with smarter initial parameters and / or with\n" << "increased initial scaling factor and / or with\n" << "increased max. number of iterations."; warning = 3; break; case 4: str_info << "\nSingular matrix. Restart from current parameters\n" << "with increased initial scaling factor."; warning = 4; break; case 5: str_info << "\nNo further error reduction is possible.\n" << "Restart with increased initial scaling factor."; warning = 5; break; case 6: str_info << "\nStopped by small squared error."; warning = 0; break; case 7: str_info << "\nStopped by invalid (i.e. NaN or Inf) \"func\" values.\n"; str_info << "This is a user error."; warning = 7; break; default: str_info << "\nUnknown reason for stopping the fit."; warning = -1; } if (use_scaling && !can_scale) { str_info << "\nCouldn't use scaling because one or more " << "of the parameters don't allow it."; } info=str_info.str(); return info_id[1]; }
void gaussfit_main (int nlhs, mxArray *plhs[],int nrhs, const mxArray *prhs[], char* variant, char* model, const int m, const int background_int, void(*fit_model)(double *p, double *x, int m, int n, void *data ), void(*fit_jacobian)(double *p, double *jac, int m, int n, void *data), const int numEqualityConstraints, const int numBounds, const int numInputs, const int numDataEntries) { //gets the dimensions of the input data (window size) int sizex = mxGetN(prhs[0]); int sizey = mxGetM(prhs[0]); //checks that input frame is square if( sizex != sizey) { mexErrMsgTxt("Input data is not a square matrix."); } //=================================== //Read in data from MATLAB //=================================== int n = sizex*sizey; //number of pixels in data //here we create a dynamic array of size N to hold the data double* x; x = new double[n]; //Read in data to x from MATLAB double *datapoint = mxGetPr(prhs[0]); for(int j = 0; j < sizey; j++) { for(int i = 0; i < sizex; i++) { x[(i * sizex) + j] = datapoint[i + (j*sizey)]; } } //Because of the way MATLAB reads mxArrays (down each column) //important to account for this because of having fixed positions etc //or fixed elliptical width. //so read the MATLAB array consecutively for speed and then the first element (1,1) goes to //position (1,1) [element 0], then the second element (1,2) goes to position (2,1) [element 1*sizex] etc //so we fill x[] down the "columns" in the array //eg for a 3x3 matrix we read in elements[0 - 8] from MATLAB and input them in array element order //[0], [3], [6] (first column done), [1],[4],[7] (second), [2], [5], [8] - all done double* constraints_pointer; double *data; //read in data from MATLAB input, if required if(numDataEntries == 0) { data = NULL; } else { data = new double[numDataEntries]; for(int i = 0; i < numDataEntries; i++) { constraints_pointer = mxGetPr(prhs[i+1]); //+1 because array of intensity data is indexed at [0] data[i] = constraints_pointer[0]; } } //data contains any constants we need to pass to the constraints functions //data contains any constants we need to pass to the constraints functions //Read in bounding condition data from MATLAB to pass to constraints function //Matlab input convention - data first, then all fixed parameters, then all bounded parameters in order - //lower bound first, then upper bound, with standard variable sequence A, sigma, b, Xo, Yo, then extra options. So bound indices for // prhs[] start at numEqualityConstraints + 1 ([0] is always the data) //=================================== //Sets up initial guess and boundaries for solver //=================================== double* p; p = new double[m]; double back_p[1]; void initialfit (char *variant, double p[], double data_array[], int n, double *data); //define initial fit function //if flag in input is set to true, use supplied guess, otherwise run initial fit routine bool* use_initial_fit_guess = mxGetLogicals(prhs[numInputs-3] ); if (use_initial_fit_guess[0] == true) //then use the guess given in input { double* init_guess_pointer = mxGetPr(prhs[numEqualityConstraints + numBounds + 2]); //gets pointer to guess for(int i = 0; i < m; i++) { p[i] = init_guess_pointer[i]; } //transfer guess from input argument } else //run the initial fits program to generate the initial guess {initialfit(variant, p, x, n, data);} //sets the initial value for the background to be used by the background only solver back_p[0] = p[background_int - 1]; //initialise boundary and penalty weighting arrays //which will be taken as the arguments for constraint calculating //and solver functions - constraints() and dlevmar_etc() double* lb; double* ub; lb = new double[m]; ub = new double[m]; //lower and upper boundary arrays double* weights; weights = new double[m]; //Not actually used but if we want to define weighting for penalty function //for use in the boundary conditions, this is here double info[LM_INFO_SZ]; //Read in search stopping parameters from input array from Matlab double* options_pointer = mxGetPr(prhs[numEqualityConstraints+numBounds+3]); double searchstopParams[4] = {options_pointer[1], options_pointer[2], options_pointer[3]}; int numIterations = options_pointer[4]; //this array controls parameters epsilon 1,2 and 3 as described in http://www.ics.forth.gr/~lourakis/levmar/levmar.pdf //the purpose of them is to stop the solver before it has reached its maximum number of iterations if the solution has //already sufficiently converged //for data with reasonable signal to noise, sufficient convergence should only take ~10 iterations, so these conditions //are important //searchstopParams[0] is epsilon1, the gradient of chi squared, 10^-2 or -3 seems to work well //searchstopParams[1] is epsilon2, the gradient of the change in best fit parameters, relative to the values of those parameters //this is generally the condition that will determine the speed/accuracy/precision of the output and is worth playing around with //higher values generally slow down the routine, but increase the accuracy, especially for badly scaled conditions (eg. background //several orders of magnitude higher than peak amplitude) good values seem to be in the range 10^-3 (for well scaled conditions) to //10^-5 //searchstopParams[2] is epsilon3, the absolute value of chi squared. For a well solved gaussian with low noise //this will still be a large number so this is essentially ignored double opts[LM_OPTS_SZ] = {LM_INIT_MU, searchstopParams[0],searchstopParams[1],searchstopParams[2], LM_DIFF_DELTA}; //opts[0] is initial mu value - controls the damping in the LM algorithm - using default //opts[1] is stopping threshold on gradient of chi square //opts[2] is stopping threshold on relative change of parameter magnitudes //opts[3] is stopping threshold on value of chi squared //opts[4] is irrelevant if analytic jacobian used, so use default //=================================== //define constraint setting function void constraints(char *variant, double *lb, double *ub, double *constraintMatrix, double *constraintRHS, int numBounds, int numEqualityConstraints, double *weights, int m, int framesize, double *data); int ret; //We will always have box (inequality constraints) eg. amplitude must be positive //However, in some cases we will have linear (equality) constraints and will need to use dlevmar_blec_der //But, if we have no equality constraints we should use dlevmar_bc_der which uses box constraints only //So use an if-else condition on numEqualityConstraints to decide which to us if(numEqualityConstraints > 0) //then we need box and linear constraints { double *ConstraintMatrix = new double[numEqualityConstraints * m]; double *ConstraintRHS = new double[numEqualityConstraints]; ///constraints function constraints(variant, lb, ub, ConstraintMatrix, ConstraintRHS, numBounds, numEqualityConstraints, weights, m, sizex, data); ///main solver function if you need equality constraints ret = dlevmar_blec_der ///basics (fit_model, //pointer to the function that calculates the model values at each datapoint given parameters p fit_jacobian, //pointer to the function that calculates the analytic jacobian dxi/dpj at each datapoint p, //current best guess parameters as an array of size m x, //data to be fitted to the model, array of size n m, //number of free parameters n, //number of datapoints ///constraints lb, //array of m elements setting lower bounds on each parameter (p[i] >= lb[i]) ub, //array of m elements setting upper bounds on each parameter (p[i] <= ub[i]) ConstraintMatrix, //matrix of equality constraint values, numEqualityconstraints = number of rows //m is number of columns ConstraintRHS, //RHS of constraint equation ConstraintMatrix * p = ConstraintRHS numEqualityConstraints, //how many constraints you have NULL, //penalty weighting array, set to NULL to use defaults, otherwise fill and use weights[m] ///solver stop procedure and debugging numIterations, //max no. of iterations the solver will run for, 50 to 100 seems about right opts, //5 element array, described above info, //from source code of levmar library: /* info: information regarding the minimization. Used to find the lsq minimisation value at output * info[0]= ||e||_2 at initial p. * info[1-4]=[ ||e||_2, ||J^T e||_inf, ||Dp||_2, mu/max[J^T J]_ii ], all computed at estimated p. * info[5]= # iterations, * info[6]=reason for terminating: 1 - stopped by small gradient J^T e * 2 - stopped by small Dp * 3 - stopped by itmax * 4 - singular matrix. Restart from current p with increased mu * 5 - no further error reduction is possible. Restart with increased mu * 6 - stopped by small ||e||_2 * 7 - stopped by invalid (i.e. NaN or Inf) "func" values. This is a user error * info[7]= # function evaluations * info[8]= # Jacobian evaluations * info[9]= # linear systems solved, i.e. # attempts for reducing error */ NULL, // working memory at least LM_BLEC_DER_WORKSZ() reals large, allocated if NULL NULL, //Covariance matrix corresponding to LS solution; mxm. Set to NULL if not needed. data); // pointer to additional data passed to function and jacobian calculator - fixed constants etc delete [] ConstraintMatrix; delete [] ConstraintRHS; } ///main solver function if you only need inequality constraints - see above for arg details else if(numEqualityConstraints == 0) //we only need box constraints { ///constraints function, inequality constraints only constraints(variant, lb, ub, NULL, NULL, numBounds, numEqualityConstraints, weights, m, sizex, data); ///main solver, see above for arg descriptions ret = dlevmar_bc_der (fit_model, fit_jacobian, p, x, m, n, lb, ub, NULL, numIterations, opts, info, NULL, NULL, data); // with analytic Jacobian } else { mexErrMsgTxt("Number of equality constraints not set properly, should be an integer >= 0"); } ///OUTPUT //output brightness, final fit parameters and normalised least squares value to matlab double brightness, lsq_normalised; const double pi = 3.14159265358979323846; //easier to do this than require a library Pi function bool got_signal = true; //assume signal as default if ( model == "circular" ) {brightness = 2.0*pi*p[0]*pow(p[1],2);} else if (model == "elliptical") { brightness = 2.0*pi*p[0]*p[1]*p[2]; } else {mexErrMsgTxt("Error, model not set.");} //if elliptical model transform the paramaters st. s_y is semimajor axis, and 0<theta<pi if(model == "elliptical") { double s_x,s_y,theta; s_x = p[1]; s_y = p[2]; theta = p[6]; if (s_x > s_y) //ie if the fit has got the axes the wrong way around { p[1] = s_y;//swap the two values p[2] = s_x; theta = theta + pi/2; //rotate the axes 90deg } //transform theta st. its 0<theta<pi ie //ie, mod(theta,pi) = theta - floor(theta/pi) * pi; theta = theta - floor(theta/pi) * pi; p[6] = theta; } lsq_normalised = info[1]/n; plhs[0] = mxCreateDoubleScalar(brightness); //send brightness as output to MATLAB plhs[1] = mxCreateDoubleMatrix(1, m, mxREAL); //need to do this next bit because can only pass an array back to Matlab as a matrix if array is //in dynamic memory via mxCalloc command void* final_fit_alloc = mxCalloc(m, sizeof(double)); double* final_fit = (double *) final_fit_alloc; for (int i = 0; i < m; i++) { final_fit[i] = p[i]; } mxFree(mxGetPr(plhs[1])); //mxSetPr will not deallocate memory allocated to plhs[1], so do it manually or get a leak mxSetPr(plhs[1],final_fit); //fit parameters are now ready to be passed to Matlab plhs[2] = mxCreateDoubleScalar(lsq_normalised); if (options_pointer[0] == true) //then display the output, otherwise just forward it to MATLAB { if (model == "circular") { mexPrintf("\nBrightness is %f.\n", brightness); mexPrintf("Fit parameters are: A = %f, sigma = %f, b = %f, Xo = %f, Yo = %f. \n", p[0],p[1],p[2],p[3],p[4]); mexPrintf("Normalised lsq is %f.\n", lsq_normalised); } if (model == "elliptical") //then display the output, otherwise just forward it to MATLAB { mexPrintf("\nBrightness is %f.\n", brightness); mexPrintf("Fit parameters are: A = %f, sigma = %f, %f, b = %f, Xo = %f, Yo = %f. Theta = %f. \n", p[0],p[1],p[2],p[3],p[4],p[5],p[6]); mexPrintf("Normalised lsq is %f.\n", lsq_normalised); } //******************Uncomment for extra information*************************************** mexPrintf("Levenberg-Marquardt returned in %g iter, reason %g, sumsq %g [%g]\n", info[5], info[6], info[1], info[0]); // Debugging - info[0] returns lsq at start of fit,info[1] returns lsq at end of fit, //******************Uncomment for extra information*************************************** } //freeup memory allocated for dynamic arrays delete [] x; delete [] p; delete [] data; delete [] lb; delete [] ub; delete [] weights; return; }