#ifndef SYNTHESIS_CPPSOLVERS_H #define SYNTHESIS_CPPSOLVERS_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace casa { //# NAMESPACE CASA - BEGIN class ParamObj { private: int nX; int nY; unsigned int AspLen; casacore::Matrix itsMatDirty; casacore::Matrix itsPsfFT; std::vector center; //genie //Eigen::Matrix newResidual; casacore::Matrix newResidual; casacore::Matrix AspConvPsf; casacore::Matrix dAspConvPsf; casacore::FFTServer fft; casacore::Matrix Asp; casacore::Matrix dAsp; public: ParamObj(const casacore::Matrix& dirty, const casacore::Matrix& psf, const std::vector& positionOptimum) : itsMatDirty(dirty), itsPsfFT(psf), center(positionOptimum)/*, newResidual(dirty)*/ { nX = itsMatDirty.shape()(0); nY = itsMatDirty.shape()(1); AspLen = center.size(); //genie //newResidual = Eigen::MatrixXf::Zero(nX, nY); newResidual.resize(nX, nY); AspConvPsf.resize(nX, nY); dAspConvPsf.resize(nX, nY); fft = casacore::FFTServer(itsMatDirty.shape()); Asp.resize(nX, nY); dAsp.resize(nX, nY); } ~ParamObj() = default; casacore::Matrix getterDirty() { return itsMatDirty; } casacore::Matrix getterPsfFT() { return itsPsfFT; } std::vector getterCenter() {return center;} unsigned int getterAspLen() { return AspLen; } int getterNX() { return nX; } int getterNY() { return nY; } // genie //Eigen::Matrix getterRes() { return newResidual; } //void setterRes(const Eigen::Matrix& res) { newResidual = res; } casacore::Matrix getterRes() { return newResidual; } void setterRes(const casacore::Matrix& res) { newResidual = res; } casacore::Matrix getterAspConvPsf() { return AspConvPsf; } void setterAspConvPsf(const casacore::Matrix& m) { AspConvPsf = m; } casacore::Matrix getterDAspConvPsf() { return dAspConvPsf; } casacore::FFTServer getterFFTServer() { return fft; } casacore::Matrix getterAsp() { return Asp; } void setterAsp(const casacore::Matrix& m) { Asp = m; } casacore::Matrix getterDAsp() { return dAsp; } }; } // end namespace casa namespace { // objective fucntion double my_f (const gsl_vector *x, void *params) { // retrieve params for GSL bfgs optimization casa::ParamObj *MyP = (casa::ParamObj *) params; //re-cast back to ParamObj to retrieve images casacore::Matrix itsMatDirty(MyP->getterDirty()); casacore::Matrix itsPsfFT(MyP->getterPsfFT()); std::vector center = MyP->getterCenter(); const unsigned int AspLen = MyP->getterAspLen(); const int nX = MyP->getterNX(); const int nY = MyP->getterNY(); //genie //Eigen::Matrix newResidual = MyP->getterRes(); casacore::Matrix newResidual(MyP->getterRes()); casacore::FFTServer fft = MyP->getterFFTServer(); casacore::Matrix AspConvPsf(MyP->getterAspConvPsf()); casacore::Matrix Asp(MyP->getterAsp()); //Eigen::Matrix AmpAspConvPsfSum = Eigen::MatrixXf::Zero(nX, nY); double fx = 0.0; double amp = 1; const int refi = nX/2; const int refj = nY/2; int minX = nX - 1; int maxX = 0; int minY = nY - 1; int maxY = 0; // First, get the amp * AspenConvPsf for each Aspen to update the residual for (unsigned int k = 0; k < AspLen; k ++) { amp = gsl_vector_get(x, 2*k); double scale = gsl_vector_get(x, 2*k+1); //std::cout << "f: amp " << amp << " scale " << scale << std::endl; if (isnan(amp) || scale < 0.4) // GSL scale < 0 { //std::cout << "nan? " << amp << " neg scale? " << scale << std::endl; // If scale is small (<0.4), make it 0 scale to utilize Hogbom and save time scale = (scale = fabs(scale)) < 0.4 ? 0 : scale; //std::cout << "reset neg scale to " << scale << std::endl; if (scale <= 0) return fx; } // generate a gaussian for each Asp in the Aspen set // x[0]: Amplitude0, x[1]: scale0 // x[2]: Amplitude1, x[3]: scale1 // x[2k]: Amplitude(k), x[2k+1]: scale(k+1) //casacore::Matrix Asp(nX, nY); Asp = 0.0; const double sigma5 = 5 * scale / 2; const int minI = std::max(0, (int)(center[k][0] - sigma5)); const int maxI = std::min(nX-1, (int)(center[k][0] + sigma5)); const int minJ = std::max(0, (int)(center[k][1] - sigma5)); const int maxJ = std::min(nY-1, (int)(center[k][1] + sigma5)); if (minI < minX) minX = minI; if (maxI > maxX) maxX = maxI; if (minJ < minY) minY = minJ; if (maxJ > maxY) maxY = maxJ; //std::cout << "minI " << minI << " minJ " << minJ << " minX " << minX << " minY " << minY << std::endl; for (int j = minJ; j <= maxJ; j++) { for (int i = minI; i <= maxI; i++) { const int px = i; const int py = j; Asp(i,j) = (1.0/(sqrt(2*M_PI)*fabs(scale)))*exp(-(pow(i-center[k][0],2) + pow(j-center[k][1],2))*0.5/pow(scale,2)); } } casacore::Matrix AspFT; //casacore::FFTServer fft(itsMatDirty.shape()); fft.fft0(AspFT, Asp); casacore::Matrix cWork; cWork = AspFT * itsPsfFT; ////casacore::Matrix AspConvPsf(itsMatDirty.shape(), (casacore::Float)0.0); /* start = std::chrono::high_resolution_clock::now(); casacore::Matrix AspConvPsf(itsMatDirty.shape(), AspConvPsfPtr, casacore::TAKE_OVER); stop = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration_cast(stop - start) ; std::cout << "BFGS AspConvPsf takeover runtime " << duration.count() << " us" << std::endl;*/ fft.fft0(AspConvPsf, cWork, false); fft.flip(AspConvPsf, false, false); //need this /*casacore::Bool ddelc; const casacore::Float *dptrc = AspConvPsf.getStorage(ddelc); float *ddptrc = const_cast(dptrc); Eigen::MatrixXf MAspConvPsf = Eigen::Map>(ddptrc, nX, nY); AmpAspConvPsfSum = AmpAspConvPsfSum + amp * MAspConvPsf; //optimumstrength*PsfConvAspen AspConvPsf.freeStorage(dptrc, ddelc);*/ // correct way is to do it here, but since we only have one aspen, it doesn't matter. /*for (int j = minJ; j < maxJ; ++j) { for(int i = minI; i < maxI; ++i) { AmpAspConvPsfSum(i, j) += amp * AspConvPsf(i, j); } }*/ // save this for my_df MyP->setterAsp(Asp); MyP->setterAspConvPsf(AspConvPsf); } // end get amp * AspenConvPsf // Update the residual using the current residual image and the latest Aspen. // Sanjay used, Res = OrigDirty - active-set aspen * Psf, in 2004, instead. // Both works but the current approach is simpler and performs well too. /*casacore::Bool ddel; const casacore::Float *dptr = itsMatDirty.getStorage(ddel); float *ddptr = const_cast(dptr); Eigen::MatrixXf Mdirty = Eigen::Map>(ddptr, nX, nY); newResidual = Mdirty - AmpAspConvPsfSum; itsMatDirty.freeStorage(dptr, ddel); // generate ChiSq // returns the objective function value for (int j = minY; j < maxY; ++j) { for(int i = minX; i < maxX; ++i) { fx = fx + double(pow(newResidual(i, j), 2)); } }*/ //genie for (int j = minY; j < maxY; ++j) { for(int i = minX; i < maxX; ++i) { newResidual(i, j) = itsMatDirty(i, j) - amp * AspConvPsf(i, j); fx = fx + double(pow(newResidual(i, j), 2)); } } //genie //std::cout << "after fx " << fx << std::endl; // update newResidual back to the ParamObj MyP->setterRes(newResidual); return fx; } // gradient void my_df (const gsl_vector *x, void *params, gsl_vector *grad) { casa::ParamObj *MyP = (casa::ParamObj *) params; //re-cast back to ParamObj to retrieve images casacore::Matrix itsMatDirty(MyP->getterDirty()); casacore::Matrix itsPsfFT(MyP->getterPsfFT()); std::vector center = MyP->getterCenter(); const unsigned int AspLen = MyP->getterAspLen(); const int nX = MyP->getterNX(); const int nY = MyP->getterNY(); //genie //Eigen::Matrix newResidual = MyP->getterRes(); casacore::Matrix newResidual(MyP->getterRes()); casacore::Matrix AspConvPsf(MyP->getterAspConvPsf()); casacore::Matrix dAspConvPsf(MyP->getterDAspConvPsf()); casacore::FFTServer fft = MyP->getterFFTServer(); casacore::Matrix Asp(MyP->getterAsp()); casacore::Matrix dAsp(MyP->getterDAsp()); // gradient. 0: amplitude; 1: scale // returns the gradient evaluated on x for (unsigned int k = 0; k < AspLen; k ++) { //casacore::Matrix Asp(nX, nY); //Asp = 0.0; //casacore::Matrix dAsp(nX, nY); dAsp = 0.0; double amp = gsl_vector_get(x, 2*k); double scale = gsl_vector_get(x, 2*k+1); //std::cout << "grad: amp " << amp << " scale " << scale << std::endl; if (isnan(amp) || scale < 0.4) // GSL scale < 0 { //std::cout << "grad: nan? " << amp << " neg scale? " << scale << std::endl; scale = (scale = fabs(scale)) < 0.4 ? 0.4 : scale; // This cannot be 0 for df to work //std::cout << "reset neg scale to " << scale << std::endl; } const double sigma5 = 5 * scale / 2; const int minI = std::max(0, (int)(center[k][0] - sigma5)); const int maxI = std::min(nX-1, (int)(center[k][0] + sigma5)); const int minJ = std::max(0, (int)(center[k][1] - sigma5)); const int maxJ = std::min(nY-1, (int)(center[k][1] + sigma5)); for (int j = minJ; j <= maxJ; j++) { for (int i = minI; i <= maxI; i++) { const int px = i; const int py = j; //Asp(i,j) = (1.0/(sqrt(2*M_PI)*fabs(scale)))*exp(-(pow(i-center[k][0],2) + pow(j-center[k][1],2))*0.5/pow(scale,2)); dAsp(i,j)= Asp(i,j) * (((pow(i-center[k][0],2) + pow(j-center[k][1],2)) / pow(scale,2) - 1) / fabs(scale)); // verified by python } } /*casacore::Matrix AspFT; fft.fft0(AspFT, Asp); casacore::Matrix cWork; cWork = AspFT * itsPsfFT; //casacore::Matrix AspConvPsf(itsMatDirty.shape(), (casacore::Float)0.0); fft.fft0(AspConvPsf, cWork, false); fft.flip(AspConvPsf, false, false); //need this */ casacore::Matrix dAspFT; fft.fft0(dAspFT, dAsp); casacore::Matrix dcWork; /*start = std::chrono::high_resolution_clock::now(); double **array1; array1 = static_cast(calloc(nX * nY, sizeof(double *))); stop = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration_cast(stop - start); std::cout << "array " << array1[1] << " BFGS calloc runtime " << duration.count() << " ms" << std::endl;*/ dcWork = dAspFT * itsPsfFT; //casacore::Matrix dAspConvPsf(itsMatDirty.shape(), (casacore::Float)0.0); fft.fft0(dAspConvPsf, dcWork, false); fft.flip(dAspConvPsf, false, false); //need this //casacore::Matrix GradAmp(itsMatDirty.shape(), (casacore::Float)0.0); //casacore::Matrix GradScale(itsMatDirty.shape(), (casacore::Float)0.0); // reset grad to 0. This is important to get the correct optimization. double dA = 0.0; double dS = 0.0; //std::cout << "before grad " << 2*k << ": " << gsl_vector_get(grad, 2*k) << std::endl; //std::cout << "before grad " << 2*k+1 << ": " << gsl_vector_get(grad, 2*k+1) << std::endl; for (int j = minJ; j <= maxJ; j++) { for (int i = minI; i <= maxI; i++) { /* // generate derivatives of amplitude GradAmp(i,j) = (-2) * newResidual(i,j) * AspConvPsf(i,j); // generate derivative of scale GradScale(i,j) = (-2) * amp * newResidual(i,j) * dAspConvPsf(i,j); dA += double(GradAmp(i,j)); dS += double(GradScale(i,j));*/ // derivatives of amplitude dA += double((-2) * newResidual(i,j) * AspConvPsf(i,j)); // derivative of scale dS += double((-2) * amp * newResidual(i,j) * dAspConvPsf(i,j)); } } gsl_vector_set(grad, 2*k, dA); gsl_vector_set(grad, 2*k+1, dS); // the following scale up doesn't seem necessary since after opt scale is back to initial guess //gsl_vector_set(grad, 2*k, dA * 1e2); //G55 test scale up //gsl_vector_set(grad, 2*k+1, dS * 1e6); //G55 test scale up //std::cout << "after grad " << 2*k << ": " << gsl_vector_get(grad, 2*k) << " amp " << amp << std::endl; //std::cout << "after grad " << 2*k+1 << ": " << gsl_vector_get(grad, 2*k+1) << " scale " << scale << std::endl; } // end of derivatives } void my_fdf (const gsl_vector *v, void *params, double *f, gsl_vector *df) { //auto start = std::chrono::high_resolution_clock::now(); *f = my_f(v, params); // has to be double in GSL /*auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(stop - start); std::cout << "BFGS my_f runtime " << duration.count() << " us" << std::endl;*/ my_df(v, params, df); } void debug_print(const gsl_multimin_fdfminimizer *s, const int k) { const gsl_vector *x = NULL; const gsl_vector *g = NULL; std::cout << "At iteration k = " << k << std::endl; g = gsl_multimin_fdfminimizer_gradient(s); std::cout << "g = " << gsl_vector_get(g, 0) << " " << gsl_vector_get(g, 1) << std::endl; x = gsl_multimin_fdfminimizer_x(s); std::cout << "x = " << gsl_vector_get(x, 0) << " " << gsl_vector_get(x, 1) << std::endl; std::cout << "f(x) = " << gsl_multimin_fdfminimizer_minimum(s) << std::endl; } int findComponent(int NIter, gsl_multimin_fdfminimizer *s) { int iter = 0; int status = 0; do { // Make the move! status = gsl_multimin_fdfminimizer_iterate(s); /*if (status == GSL_ENOPROG) // 27: not making progress towards solution gsl_multimin_fdfminimizer_restart(s);*/ if (status) break; status = gsl_multimin_test_gradient(s->gradient, 1E-3); // debug_print(s, iter); iter++; } while(status == GSL_CONTINUE && iter < NIter); return status; } } // end namespace #endif