//# CEMemModel.h: this defines CEMemModel //# Copyright (C) 1996,1997,1998,1999,2000 //# Associated Universities, Inc. Washington DC, USA. //# //# This library is free software; you can redistribute it and/or modify it //# under the terms of the GNU Library General Public License as published by //# the Free Software Foundation; either version 2 of the License, or (at your //# option) any later version. //# //# This library is distributed in the hope that it will be useful, but WITHOUT //# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or //# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public //# License for more details. //# //# You should have received a copy of the GNU Library General Public License //# along with this library; if not, write to the Free Software Foundation, //# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA. //# //# Correspondence concerning AIPS++ should be addressed as follows: //# Internet email: aips2-request@nrao.edu. //# Postal address: AIPS++ Project Office //# National Radio Astronomy Observatory //# 520 Edgemont Road //# Charlottesville, VA 22903-2475 USA //# //# $Id$ #ifndef SYNTHESIS_CEMEMMODEL_H #define SYNTHESIS_CEMEMMODEL_H #include <casa/aips.h> #include <casa/Arrays/Matrix.h> #include <casa/Arrays/Vector.h> #include <casa/Arrays/Array.h> #include <lattices/Lattices/Lattice.h> #include <images/Images/PagedImage.h> #include <synthesis/MeasurementEquations/Entropy.h> #include <synthesis/MeasurementEquations/LinearEquation.h> #include <synthesis/MeasurementEquations/LinearModel.h> #include <casa/Logging/LogIO.h> namespace casa { //# NAMESPACE CASA - BEGIN // Forward declaration class LatConvEquation; class CEMemProgress; // <summary> Implements the Cornwell & Evans MEM Algorithm on Lattices </summary> // <use visibility=export> // <reviewed reviewer="" date="yyyy/mm/dd" tests="" demos=""> // </reviewed> // <prerequisite> // <li> ResidualEquation/ConvolutionEquation // <li> LinearModel/LinearEquation Paradigm // </prerequisite> // // <etymology> // This class is called CEMemModel because it uses the Cornwell and // Evans MEM algorithm to deconvolve the model. // </etymology> // // <synopsis> // This class is used to perform the Cornwell and Evans MEM Algorithm on an // Array. Only the deconvolved model of the sky are directly stored by this // class. The point spread function (psf) and convolved (dirty) image are // stored in a companion class which is must be derived from // ResidualEquation. // // The deconvolution works like this. The user constructs a CEMemModel by // specifying an initial model of the sky. This can by be // one,two,three... dimensional depending on the dimension of the psf (see // below). The user then constructs a class which implements the forward // equation between the model and the dirty image. Typically this will be // the ConvolutionEquation class, although any class which has a // ResidualEquation interface will work (but perhaps very slowly, as the // ConvolutionEquation class has member functions optimised for CLEAN and MEM) // // The user then calls the solve() function (with the appropriate equation // class as an arguement), and this class will perform the MEM deconvolution. // The various MEM parameters are set (prior to calling solve) using the // functions derived from the Iterate class, in particular // setNumberIterations(). // // The solve() function does not return either the deconvolved model or the // residuals. The solved model can be obtained using the getModel() function // (derived from ArrayModel()) and the residual can be obtained using the // residual() member function of the Convolution/Residual Equation Class. // // The size and shape of the model used in this class MUST be the same as // the convolved data (Dirty Image), stored in the companion // ResidualEquation Class. However the model (and convolved data) can have // more dimensions than the psf, as well as a different size (either larger // or smaller). When the dimensionality is different the deconvolution is done // independendtly in each "plane" of the model. (Note this has not // been implemented yet but is relatively simple to do if necessary). // // StokesVectors are not yet implemented. // // A companion class to this one is MaskedCEMemModel. This provides // the same functionality but is used with MaskedArrays which indicate which // regions of the NewtonRaphson (residual) image to apply when forming the // step image (MaskedCEMemModel is not yet implemented). // // </synopsis> // // <example> // <srcblock> // casacore::Matrix<casacore::Float> psf(12,12), dirty(10,10), initialModel(10,10); // ...put appropriate values into psf, dirty, & initialModel.... // CEMemModel<casacore::Float> deconvolvedModel(initialModel); // ConvolutionEquation convEqn(psf, dirty); // deconvolvedModel.setSigma(0.001); // deconvolvedModel.setTargetFlux(-2.500); // deconvolvedModel.setNumberIterations(30); // casacore::Bool convWorked = deconvolvedModel.solve(convEqn); // casacore::Array<casacore::Float> finalModel, residuals; // if (convWorked){ // finalModel = deconvolvedModel.getModel(); // ConvEqn.residual(deconvolvedModel, finalResidual); // } // </srcblock> // </example> // // <motivation> // This class is needed to deconvolve extended images. // In time, the MEM algorithm will be a principle player in the // mosaicing stuff. // </motivation> // // <templating arg=T> // For testing: // <li> casacore::Float: lets try it first // <li> StokesVector: will require lots more work // </templating> // // <todo asof="1998/12/02"> // <li> We need to implement soft Masking // </todo> class CEMemModel: public LinearModel< casacore::Lattice<casacore::Float> > { // Any new entropies derived from Entropy must sign the friend list: friend class Entropy; friend class EntropyI; friend class EntropyEmptiness; public: // Construct the CEMemModel object and initialise the model. CEMemModel(Entropy &ent, casacore::Lattice<casacore::Float> & model, casacore::uInt nIntegrations = 10, casacore::Float sigma = 0.001, casacore::Float targetFlux = 1.0, casacore::Bool useFluxConstraint = false, casacore::Bool initializeModel = true, casacore::Bool imagePlane = false); // Construct the CEMemModel object, initialise the model and Prior images. CEMemModel(Entropy &ent, casacore::Lattice<casacore::Float> & model, casacore::Lattice<casacore::Float> & prior, casacore::uInt nIntegrations = 10, casacore::Float sigma = 0.001, casacore::Float targetFlux = 1.0, casacore::Bool useFluxConstraint = false, casacore::Bool initializeModel = true, casacore::Bool imagePlane = false); // Construct the CEMemModel object, initialise the model, Prior, // and mask images. CEMemModel(Entropy &ent, casacore::Lattice<casacore::Float> & model, casacore::Lattice<casacore::Float> & prior, casacore::Lattice<casacore::Float> & mask, casacore::uInt nIntegrations = 10, casacore::Float sigma = 0.001, casacore::Float targetFlux = 1.0, casacore::Bool useFluxConstraint = false, casacore::Bool initializeModel = true, casacore::Bool imagePlane = false); // destructor virtual ~CEMemModel(); // solve the convolution equation // returns true if converged // Gives information about the state of the CEMem // (ie, using mask image, using prior image; more work here!) void state(); // This needs to be "ResidualEquation", using LatConvEquation as // polymorphism is broken casacore::Bool solve(ResidualEquation<casacore::Lattice<casacore::Float> > & eqn); // Set and get various state images and classes // <group> // set or get the Entropy class void setEntropy(Entropy &myEntropy ) {itsEntropy_ptr = &myEntropy;} void getEntropy(Entropy &myEntropy ) {myEntropy = *itsEntropy_ptr;} // set or get the Model image casacore::Lattice<casacore::Float>& getModel() const { return (*(itsModel_ptr->clone())); } void setModel(const casacore::Lattice<casacore::Float> & model) { itsModel_ptr = model.clone(); } // set or get the Prior image casacore::Lattice<casacore::Float>& getPrior() const { return (*(itsPrior_ptr->clone())); } void setPrior(casacore::Lattice<casacore::Float> & prior); // set or get the Mask image casacore::Lattice<casacore::Float>& getMask() const { return (*(itsMask_ptr->clone())); } void setMask(casacore::Lattice<casacore::Float> & mask); // get the Residual image casacore::Lattice<casacore::Float>& getResidual() const { return (*(itsResidual_ptr->clone())); } // </group> // set and get alpha and beta // <group> casacore::Float getAlpha() const { return itsAlpha; } casacore::Float getBeta() const { return itsBeta; } void setAlpha(casacore::Float alpha) {itsAlpha = alpha; } void setBeta(casacore::Float beta) {itsBeta = beta; } // </group> // Set various controlling parameters // (The most popular controlling parameters are // set in the constructor) // <group> casacore::Float getTolerance() {return itsTolerance; } void setTolerance(casacore::Float x) { itsTolerance = x; } casacore::Float getQ() {return itsQ; } void setQ(casacore::Float x) { itsQ= x; } casacore::Float getGain() {return itsGain; } void setGain(casacore::Float x) { itsGain = x; } casacore::Float getMaxNormGrad() {return itsMaxNormGrad; } void setMaxNormGrad(casacore::Float x) { itsMaxNormGrad = x; } casacore::Int getInitialNumberIterations() {return itsFirstIteration; } void setInitialNumberIterations(casacore::Int x) { itsFirstIteration = x; } // </group> // The convergence can also be in terms of the maximum residual // (ie, for use in stopping in a major cycle). void setThreshold (const casacore::Float x ) { itsThreshold0 = x; } // Thresh doubles in iter iterations void setThresholdSpeedup (const casacore::Float iter) {itsThresholdSpeedup = iter; } casacore::Float getThreshold(); // Set/get the progress display // <group> virtual void setProgress(CEMemProgress& ccp) { itsProgressPtr = &ccp; } virtual CEMemProgress& getProgress() { return *itsProgressPtr; } // </group> // return the number of iterations completed casacore::Int numberIterations () { return itsIteration; } // if this MEMModel is constructed in a MF loop, we may need to // increment the flux by the last iterations flux void setCycleFlux(casacore::Float x) { itsCycleFlux = x; } casacore::Float getCycleFlux() { return itsCycleFlux; } protected: // Perform One Iteration void oneIteration(); // apply mask to a lattice; returns true if mask is available, // false if not casacore::Bool applyMask( casacore::Lattice<casacore::Float> & lat ); // Helper functions that interface with Entropy routines // Access to the entropy should be through this interface; the // points at which the Entropy is mentioned is then limited to // these lines right here, and to the constructors which set the // Entropy. The entropy should not ever change the private // // <group> void formEntropy() { itsEntropy = itsEntropy_ptr->formEntropy(); } void formGDG() { itsEntropy_ptr->formGDG( itsGDG ); } void formGDGStep() { itsEntropy_ptr->formGDGStep( itsGDG ); } void formGDS() { itsGradDotStep1=itsEntropy_ptr->formGDS(); } void entropyType(casacore::String &str) { itsEntropy_ptr->entropyType(str); } void relaxMin() { itsRequiredModelMin = itsEntropy_ptr->relaxMin(); } casacore::Bool testConvergence() { return itsEntropy_ptr->testConvergence(); } // </group> // protected generic constrcutor: DON'T USE IT! CEMemModel(); // Set entropy pointer to zero: called by Entropy's destructor void letEntropyDie() { itsEntropy_ptr = 0; } // initialize itsStep and itsResidual and other stuff casacore::Bool initStuff(); // controls how to change Alpha and Beta // <group> void changeAlphaBeta (); // initialize Alpha-Beta (called by changeAlphaBeta) void initializeAlphaBeta(); // update Alpha-Beta (called by changeAlphaBeta) void updateAlphaBeta(); // </group> // Generic utility functions // <group> // check that a single image is onf plausible shape casacore::Bool checkImage(const casacore::Lattice<casacore::Float> *); // check that the lattices and the underlying tile sizes are consistent casacore::Bool checkImages(const casacore::Lattice<casacore::Float> *one, const casacore::Lattice<casacore::Float> *other); // check that all is well in Denmark: // ensure all images are the same size, // ensure we have an entropy, // ensure state variables have reasonable values casacore::Bool ok(); // </group> // Helper functions for oneIteration: // <group> // calculate size of step void calculateStep(); // take one step: clipped addition of // wt1*itsModel + wt2*itsStep void takeStep(casacore::Float wt1, casacore::Float wt2); // Calculate the flux, itsModMin, and itsModMax casacore::Float formFlux(); // </group> // Determine if the peak residual is less than the getThreshold() casacore::Bool testConvergenceThreshold(); // ------------Private Member casacore::Data--------------------- // functional form of the entropy Entropy *itsEntropy_ptr; // form of the Residual method ResidualEquation< casacore::Lattice<casacore::Float> > * itsResidualEquation_ptr; // Images: casacore::Lattice<casacore::Float> * itsModel_ptr; casacore::Lattice<casacore::Float> * itsPrior_ptr; casacore::Lattice<casacore::Float> * itsMask_ptr; // Our OWN internal temp images; delete these upon destruction casacore::Lattice<casacore::Float> * itsStep_ptr; casacore::Lattice<casacore::Float> * itsResidual_ptr; // Controlling parameters // <group> casacore::Bool itsInitializeModel; casacore::uInt itsNumberIterations; casacore::Bool itsDoInit; casacore::Float itsSigma; casacore::Float itsTargetFlux; casacore::Float itsDefaultLevel; casacore::Float itsTargetChisq; // tolerance for convergence casacore::Float itsTolerance; // N points per beam casacore::Float itsQ; // gain for adding step image casacore::Float itsGain; casacore::Float itsMaxNormGrad; // constrain flux or not? casacore::Bool itsUseFluxConstraint; // is this an image plane problem (like Single Dish or Optical?) casacore::Bool itsDoImagePlane; casacore::Float itsThreshold0; casacore::Float itsThresholdSpeedup; casacore::Float itsCycleFlux; // flux from previous cycles // </group> // State variables // <group> casacore::Float itsAlpha; casacore::Float itsBeta; casacore::Float itsNormGrad; casacore::Float itsFlux; casacore::Float itsTotalFlux; // itsCycleFlux + itsFlux casacore::Float itsChisq; // sqrt( chi^2/target_chi^2 ) casacore::Float itsFit; // sqrt( chi^2/ Npixels ) casacore::Float itsAFit; // numerical value of entropy casacore::Float itsEntropy; // Model is constrained to be >= itsNewMin casacore::Float itsRequiredModelMin; // maximum pixel value in model casacore::Float itsModelMax; // minimum pixel value n model casacore::Float itsModelMin; casacore::Float itsLength; casacore::Double itsGradDotStep0; casacore::Double itsGradDotStep1; casacore::uInt itsIteration; casacore::uInt itsFirstIteration; // matrix of gradient dot products casacore::Matrix<casacore::Double> itsGDG; casacore::Float itsCurrentPeakResidual; // </group> casacore::Float itsNumberPixels; // Accesories // <group> casacore::Bool itsChoose; casacore::LogIO itsLog; // </group> // Enumerate the different Gradient subscript types enum GradientType { // Entropy H=0, // Chi_sq C=1, // Flux F=2, // Objective function J J=3 }; CEMemProgress* itsProgressPtr; }; } //# NAMESPACE CASA - END #endif