9 #include "sOutputManager.hh" 222 cout <<
"This optimizer is the ADMM with non-negativity constraint on projection space." << endl;
223 cout <<
"It was proposed by H. Lim, Y. K. Dewaraja, and J. A. Fessler, Physics " << endl;
224 cout <<
"in Medicine & Biology, 2018. It is for now developed without subsets" << endl;
225 cout <<
"It is implemented using the same equations as in the original paper, also with one inner iteration." << endl;
226 cout <<
"The algorithm can be used without penalty, with quadratic penalty or with Markov Random Field (MRF) penalty with quadratic potential." << endl;
227 cout <<
"The following options can be used according to the paper by B. Wohlberg in 2017 on residual balancing:" << endl;
228 cout <<
" - alpha: to set the convergence speed of the ADMM with penalty parameter alpha." << endl;
229 cout <<
" - adaptive : to set which parameters are adaptive (must be set to nothing, alpha, or tau (which means alpha and tau))." << endl;
230 cout <<
" - mu: factor to balance primal and dual residual in adaptive alpha computation." << endl;
231 cout <<
" - tau: Factor to multiply alpha in adaptive alpha computation." << endl;
232 cout <<
" - xi: Factor to balance primal and dual residual convergence speed in adaptive tau computation." << endl;
233 cout <<
" - tau_max: Maximum value of the tau factor to multiply alpha in adaptive alpha and tau computation." << endl;
234 cout <<
" - stoppingCriterionValue: Error value for a stopping criterion based on small residuals. 0 means no stopping criterion." << endl;
235 cout <<
" - saveSinogramsUAndV: to save or not the computed sinograms u and v for same iterations as image " << endl;
236 cout <<
" (sinograms u and v can only be saved for non TOF data)" << endl;
237 cout <<
"Adviced practice is to use adaptive tau with xi to 1 or to use adaptive alpha with mu to 10 and tau to 2." << endl;
238 cout <<
"A stopping criterion can be used (both residuals smaller than 1e-4 for instance)." << endl;
239 cout <<
"This optimizer was extended from Lim et al. paper to dynamic frame by frame reconstruction, and TOF reconstruction. However, " << endl;
240 cout <<
"only one alpha value can be derived from ADMM as the likelihood is maximized over all the frames/TOF bins at the same time." << endl;
241 cout <<
"Adviced practice is to launch a frame by frame reconstruction with one CASToR command line per frame, so that alpha is adapted to the frame." << endl;
242 cout <<
"Moreover, the use of time basis functions may not make sense as, for some of them, the image can have negative voxels (as the optimizer" << endl;
243 cout <<
"allows for it), which can be meaningless from a physiological or physical point of view." << endl;
244 cout <<
"TOF reconstruction is not expected to converge quicker than non-TOF reconstruction as alpha is related to the constraint Ax=v" << endl;
245 cout <<
"which is harder to satisfy for several TOF bins." << endl;
246 cout <<
"N.B.: The first iteration will not update the image if no penalty is used or if a uniform initialization image is used with MRF penalty." << endl;
247 cout <<
"In these cases, it will only change sinograms u and v." << endl;}
256 string key_word =
"";
262 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
266 key_word =
"adaptive";
269 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
272 if (buffer ==
"nothing")
276 else if (buffer ==
"alpha")
280 else if (buffer ==
"both")
288 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
295 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
302 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
306 key_word =
"tau_max";
309 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
313 key_word =
"stoppingCriterionValue";
316 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
320 key_word =
"saveSinogramsUAndV";
323 Cerr(
"***** iOptimizerADMMLim::ReadConfigurationFile() -> Failed to get the '" << key_word <<
"' keyword !" << endl);
326 if (buffer ==
"true")
330 else if (buffer ==
"false")
346 const int nb_options = 8;
347 FLTNB options[nb_options];
349 if (
ReadStringOption(a_optionsList, options, nb_options,
",",
"ADMMLim configuration"))
351 Cerr(
"***** iOptimizerADMMLim::ReadOptionsList() -> Failed to correctly read the list of options !" << endl);
377 Cerr(
"***** iOptimizerADMMLim::CheckSpecificParameters() -> Should provide which parameters are adaptive (nothing, alpha or tau)" << endl);
383 Cerr(
"***** iOptimizerADMMLim::CheckSpecificParameters() -> Please specify if sinograms u and v need to be stored (put 0 or 1)" << endl);
389 Cerr(
"!!!!! iOptimizerADMMLim::CheckSpecificParameters() -> There are several frames or gates, thus sinograms u and v will not be stored even if asked by the user" << endl);
394 Cerr(
"***** iOptimizerADMMLim->CheckSpecificParameters() -> Provided alpha (" <<
m_alpha <<
") must be strictly positive !" << endl);
400 Cerr(
"***** iOptimizerADMMLim->CheckSpecificParameters() -> Provided mu (" <<
m_mu <<
") must be strictly positive !" << endl);
406 Cerr(
"***** iOptimizerADMMLim->CheckSpecificParameters() -> Provided tau (" <<
m_tau <<
") must be strictly greater than 1 !" << endl);
412 Cerr(
"***** iOptimizerADMMLim->CheckSpecificParameters() -> Provided xi (" <<
m_xi <<
") must be strictly positive !" << endl);
418 Cerr(
"***** iOptimizerADMMLim->CheckSpecificParameters() -> Provided tau (" <<
m_tauMax <<
") must be strictly greater than 1 !" << endl);
424 Cerr(
"***** iOptimizerADMMLim->CheckSpecificParameters() -> Provided value for the stopping criterion (" <<
m_stoppingCriterionValue <<
") must be positive !" << endl);
430 Cerr(
"!!!!! iOptimizerADMMLim::CheckSpecificParameters() -> ADMMLim does not require additional data, except if you want to initialize u^k and v^k !" << endl);
434 Cerr(
"***** iOptimizerADMMLim::CheckSpecificParameters() -> ADMMLim requires 2 additional data for u^k and v^k if they need to be initialized, or nothing !" << endl);
440 Cerr(
"***** iOptimizerADMMLim->CheckSpecificParameters() -> Cannot reconstruct list-mode or transmission data !" << endl);
464 Cerr(
"***** iOptimizerADMMLim->InitializeSpecific() -> This optimizer is only compatible with Quadratic potential function for the Markov Random Field penalty (MRF) penalty !" << endl);
465 Cerr(
" Please use 'potential function: quadratic' in the penalty initialization file!" << endl);
471 Cerr(
"***** iOptimizerADMMLim->InitializeSpecific() -> This optimizer is only compatible with the Markov Random Field (MRF) penalty, with quadratic penalty, or without any penalty !" << endl);
591 Cout(
"iOptimizerADMMLim::InitializeSpecific() -> Use the ADMMLim optimizer" << endl);
597 Cout(
" --> Adaptive mode: " <<
"not adaptive" << endl);
601 Cout(
" --> Adaptive mode: " <<
"adaptive alpha" << endl);
605 Cout(
" --> Adaptive mode: " <<
"adaptive alpha and tau" << endl);
607 Cout(
" --> Initial penalty parameter alpha: " <<
m_alpha << endl);
608 Cout(
" --> Initial penalty parameter tau: " <<
m_tau << endl);
611 Cout(
" --> Factor mu: " <<
m_mu << endl);
612 Cout(
" --> Factor xi: " <<
m_xi << endl);
616 Cout(
" --> Maximum tau value (if adaptive alpha and tau) tau_max: " <<
m_tau << endl);
620 Cout(
" --> Stopping criterion is used" << endl);
639 Cerr(
"***** iOptimizerADMMLim::PreDataUpdateSpecificStep() -> Should provide only one subset !" << endl);
679 temps_ss_stop +=
"_adaptive_stopping_criteria.log";
681 outfile.open(temps_ss_stop);
682 outfile << setprecision(numeric_limits<FLTNB>::digits10 + 1);
683 outfile <<
"final outer iteration : " << endl;
685 outfile <<
"relPrimal : " << endl;
687 outfile <<
"relDual : " << endl;
691 Cerr(
"!!!!! Stopping criterion reached ! Leaving computation." << endl);
715 #pragma omp parallel for private(v) schedule(guided) 745 Cerr(
"***** iOptimizerADMMLim::PreDataUpdateSpecificStep() -> A problem occurred while applying image convolver to gradient image !" << endl);
766 if (time_basis_coef==0.)
continue;
772 if (resp_basis_coef==0.)
continue;
778 if (card_basis_coef==0.)
continue;
780 FLTNB global_basis_coef = time_basis_coef * resp_basis_coef * card_basis_coef;
785 #pragma omp parallel for private(v) schedule(guided) 823 if (time_basis_coef==0.)
continue;
829 if (resp_basis_coef==0.)
continue;
835 if (card_basis_coef==0.)
continue;
837 FLTNB global_basis_coef = time_basis_coef * resp_basis_coef * card_basis_coef;
839 bool problem =
false;
843 #pragma omp parallel for private(v) schedule(guided) 849 th = omp_get_thread_num();
854 Cerr(
"***** iOptimizerADMMLim::PreDataUpdateSpecificStep() -> A problem occurred while computing the penalty local pre-processing step for voxel " << v <<
" !" << endl);
866 Cerr(
"***** iOptimizerADMMLim::PreImageUpdateSpecificStep() -> A problem occurred inside the multi-threaded loop, stop now !" << endl);
888 int a_bed,
int a_timeFrame,
int a_respGate,
int a_cardGate,
913 FLTNB a_multiplicativeCorrections,
FLTNB a_additiveCorrections,
FLTNB a_blankValue,
928 int a_bed,
int a_timeFrame,
int a_respGate,
int a_cardGate,
960 FLTNB a_multiplicativeCorrections,
FLTNB a_additiveCorrections,
FLTNB a_blankValue,
974 int a_bed,
int a_timeFrame,
int a_respGate,
int a_cardGate,
1013 FLTNB beta_square_gamma = beta * beta + gamma;
1022 if (beta_square_gamma < 0.)
1024 Cerr(
"!!!!! beta * beta + gamma = " << beta_square_gamma <<
" , should not be negative ! Perhaps it is a case of divergence." << endl);
1025 beta_square_gamma = 0.;
1028 v_hat = (beta < 0.) ? sqrt(beta_square_gamma) - beta :gamma / (sqrt(beta_square_gamma) + beta);
1090 FLTNB dual_norm = 0.;
1091 FLTNB AtuSquareNorm = 0.;
1092 FLTNB AtvvSquareNorm = 0.;
1114 dual_norm =
m_alpha * sqrt(AtvvSquareNorm);
1125 if (adaptiveTau >= 1 && adaptiveTau <
m_tauMax)
1127 m_tau = adaptiveTau;
1129 else if (adaptiveTau >= (1/
m_tauMax) && adaptiveTau < 1)
1131 m_tau = 1/adaptiveTau;
1167 outfile.open(temps_ss_alpha);
1168 outfile << setprecision(numeric_limits<FLTNB>::digits10 + 1);
1169 outfile <<
"adaptive alpha : " << endl;
1171 outfile <<
"adaptive tau" << endl;
1172 outfile <<
m_tau << endl;
1173 outfile <<
"relPrimal" << endl;
1175 outfile <<
"relDual" << endl;
1177 outfile <<
"primal residual" << endl;
1180 outfile <<
"dual residual" << endl;
1181 outfile << dual_norm << endl;
1193 int first_TOF_bin = 0;
1197 stringstream temp_ss_v;
1199 string a_pathToImg_v = temp_ss_v.str();
1205 stringstream temp_ss_u;
1207 string a_pathToImg_u = temp_ss_u.str();
1225 if (
m_verbose>=1)
Cout(
"iOptimizerADMMLim::PreImageUpdateSpecificStep() -> Compute penalty term" << endl);
1230 Cerr(
"***** iOptimizerADMMLim::PreImageUpdateSpecificStep() -> A problem occurred while computing the penalty pre-processing step !" << endl);
1244 bool problem =
false;
1248 #pragma omp parallel for private(v) schedule(guided) 1254 th = omp_get_thread_num();
1259 Cerr(
"***** iOptimizerADMMLim::PreImageUpdateSpecificStep() -> A problem occurred while computing the penalty local pre-processing step for voxel " << v <<
" !" << endl);
1268 Cerr(
"***** iOptimizerADMMLim::PreImageUpdateSpecificStep() -> A problem occurred inside the multi-threaded loop, stop now !" << endl);
1298 else Cout(spaces_tbf <<
"--> Time basis function: " << tbf+1 << endl);
1307 else Cout(spaces_tbf << spaces_rbf <<
"--> Respiratory basis function: " << rbf+1 << endl);
1316 else Cout(spaces_tbf << spaces_rbf << spaces_cbf <<
"--> Cardiac basis function: " << cbf+1 << endl);
1339 #pragma omp parallel for private(v) schedule(guided) 1345 th = omp_get_thread_num();
1350 if (sensitivity<=0.)
continue;
1403 *apImageValue = (
HPFLTNB)a_currentImageValue + additive_image_update_factor;
1426 if (error)
return 1;
1447 mp_imageStatMean[0] = (count1*mp_imageStatMean[0] + count2*mp_imageStatMean[th]) / count12;
1451 mp_correctionStatMean[0] = (count1*mp_correctionStatMean[0] + count2*mp_correctionStatMean[th]) / count12;
1457 Cout(spaces_tbf << spaces_rbf << spaces_cbf <<
" --> Image update step " 1460 Cout(spaces_tbf << spaces_rbf << spaces_cbf <<
" --> New image estimate" 1479 FLTNB a_sensitivity,
FLTNB* ap_correctionValues,
1480 INTNB a_voxel,
int a_tbf,
int a_rbf,
int a_cbf )
int m_requiredPenaltyDerivativesOrder
int64_t GetNbEvents()
Get the total number of events in the datafile.
#define ADMMLIM_SAVE_SINOGRAMS
#define MRF_POTENTIAL_QUADRATIC
HPFLTNB * mp_projGradSquareNorm
int ReadStringOption(const string &a_input, T *ap_return, int a_nbElts, const string &sep, const string &a_option)
Parse the 'a_input' string corresponding to the 'a_option' into 'a_nbElts' elements, using the 'sep' separator. The results are returned in the templated 'ap_return' dynamic templated array. Call "ConvertFromString()" to perform the correct conversion depending on the type of the data to convert.
int GetNbCardBasisFunctions()
Get the number of cardiac basis functions.
FLTNB GetFrameDurationInSec(int a_bed, int a_frame)
#define ADMMLIM_NOT_ADAPTIVE
int CheckSpecificParameters()
A private function used to check the parameters settings specific to the child optimizer.
int m_criterionReachedIteration
bool m_listmodeCompatibility
FLTNB GetTimeBasisCoefficient(int a_timeBasisFunction, int a_timeFrame)
oImageDimensionsAndQuantification * mp_ImageDimensionsAndQuantification
FLTNB ** m2p_additionalData
HPFLTNB * mp_squareNorm_v
FLTNB **** m4p_currentGrad
HPFLTNB * mp_gradSquareNorm
HPFLTNB * mp_primalSquareNorm
FLTNB ComputeSensitivity(FLTNB ****a4p_sensitivityImage, int a_timeBasisFunction, int a_respBasisFunction, int a_cardBasisFunction, int a_voxel)
virtual FLTNB GetAdditiveCorrections(int a_bin)=0
const string & GetPenaltyID()
bool m_firstIterationIsPassed
int ApplyConvolution(FLTNB *ap_image)
A function that simply calls the eponym function from the vImageConvolver.
iOptimizerADMMLim()
The constructor of iOptimizerADMMLim.
virtual int LocalPreProcessingStep(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)
virtual int DataStep4Optional(oProjectionLine *ap_Line, vEvent *ap_Event, int a_bed, int a_timeFrame, int a_respGate, int a_cardGate, int a_thread)
int GetNbTimeBasisFunctions()
Get the number of time basis functions.
#define ADMMLIM_ADAPTIVE_ALPHA_AND_TAU
HPFLTNB * mp_penaltyGradSquareNorm
HPFLTNB * mp_imageStatMean
FLTNB ForwardProject(oProjectionLine *ap_Line, FLTNB *ap_image=NULL)
static sOutputManager * GetInstance()
Instanciate the singleton object and Initialize member variables if not already done, return a pointer to this object otherwise.
virtual int DataStep6Optional(oProjectionLine *ap_Line, vEvent *ap_Event, int a_bed, int a_timeFrame, int a_respGate, int a_cardGate, int a_thread)
A public function to compute the analytical solution of ADMM equation on v and update u...
FLTNB GetPenaltyStrength()
Get the penalty strength.
bool m_criterionReachedIsPassed
bool GetRespStaticFlag()
Get the respiratory static flag that says if the reconstruction has only one respiratory gate or not...
FLTNB ****** m6p_backwardImage
FLTNB * GetNewAdditionalDataMatrix(INTNB a_nbDataPerEvent)
Allocate the memory for this additional data matrix and return the pointer to the matrix...
int ReadConfigurationFile(const string &a_configurationFile)
A function used to read options from a configuration file.
void ShowHelpSpecific()
A function used to show help about the child optimizer.
HPFLTNB * mp_correctionStatMean
#define ADMMLIM_NOT_DEFINED
const string & GetPathName()
INTNB * mp_imageStatNbVox
FLTNB ** m2p_forwardValues
FLTNB *** m3p_backwardValues
bool GetCardStaticFlag()
Get the cardiac static flag that says if the reconstruction has only one cardiac gate or not...
bool m_emissionCompatibility
#define ADMMLIM_DO_NOT_SAVE_SINOGRAMS
FLTNB GetRespBasisCoefficient(int a_respBasisFunction, int a_respGate)
Singleton class that manages output writing on disk (images, sinograms, etc). It also manages loggi...
virtual int GlobalPreProcessingStep()
A public function computing a global pre-processing step for the penalty.
int64_t GetEventIndex()
Get current index associated to the event.
HPFLTNB * mp_imageStatVariance
FLTNB GetCardBasisCoefficient(int a_cardBasisFunction, int a_cardGate)
bool m_histogramCompatibility
bool m_optimizerImageStatFlag
Declaration of class iOptimizerADMMLim.
virtual int DataStep5ComputeCorrections(oProjectionLine *ap_Line, vEvent *ap_Event, int a_bed, int a_timeFrame, int a_respGate, int a_cardGate, int a_thread)
A public function used to compute the correction terms in the data space, for the provided event...
bool * mp_outputIterations
FLTNB ** m2p_rBackgroundEvents
bool GetTimeStaticFlag()
Get the time static flag that says if the reconstruction has only one frame or not.
oImageSpace * mp_ImageSpace
int m_currentTotalSubIteration
FLTNB **** m4p_firstDerivativePenaltyImage
FLTNB ***** m5p_sensitivity
#define KEYWORD_MANDATORY
~iOptimizerADMMLim()
The destructor of iOptimizerADMMLim.
FLTNB m_relativePrimalSquareNorm
HPFLTNB * mp_correctionStatVariance
int GetNbAdditionalData()
Get the number of additional data.
#define SPEC_TRANSMISSION
FLTNB **** m4p_currentGradFrameGates
virtual FLTNB GetEventValue(int a_bin)=0
bool m_transmissionCompatibility
This class is designed to generically described any iterative optimizer.
This class is designed to manage and store system matrix elements associated to a vEvent...
const string & GetBaseName()
int ReadOptionsList(const string &a_optionsList)
A function used to read options from a list of options.
int GetNbCardGates()
Get the number of cardiac gates.
virtual int PreDataUpdateSpecificStep()
A private function used to compute some norms for the image update.
FLTNB m_relativeDualSquareNorm
Mother class for the Event objects.
int GetNbThreadsForImageComputation()
Get the number of threads used for image operations.
int GetNbTimeFrames()
Get the number of time frames.
INTNB GetNbVoxXYZ()
Get the total number of voxels.
int PreImageUpdateSpecificStep()
A private function used to compute the penalty term update adaptive alpha of the ADMM algorithm...
int GetNbTOFBins()
This function is used to get the number of TOF bins in use.
virtual int ImageUpdateStep()
A public function used to perform the image update step of the optimizer.
int GetNbThreadsForProjection()
Get the number of threads used for projections.
#define ADMMLIM_ADAPTIVE_ALPHA
virtual FLTNB ComputeFirstDerivative(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)=0
int InitializeSpecific()
This function is used to initialize specific stuff to the child optimizer.
int GetNbRespGates()
Get the number of respiratory gates.
int DataSpaceSpecificOperations(FLTNB a_data, FLTNB a_forwardModel, FLTNB *ap_backwardValues, FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue, FLTNB a_quantificationFactor, oProjectionLine *ap_Line)
This function performs the data space operations specific to the optimizer (computes the values to be...
void SetCurrentTOFBin(int a_TOFBin)
int ReadDataASCIIFile(const string &a_file, const string &a_keyword, T *ap_return, int a_nbElts, bool a_mandatoryFlag)
Look for "a_nbElts" elts in the "a_file" file matching the "a_keyword" string passed as parameter a...
HPFLTNB * mp_squareNorm_Ax
FLTNB m_stoppingCriterionValue
int IntfWriteImage(const string &a_pathToImg, FLTNB *ap_outImgMtx, uint32_t a_dim, int vb)
Write Interfile raw data whose path is provided in parameter, using image matrix provided in paramete...
int ImageSpaceSpecificOperations(FLTNB a_currentImageValue, FLTNB *apImageValue, FLTNB a_sensitivity, FLTNB *ap_correctionValues, INTNB a_voxel, int a_tbf=-1, int a_rbf=-1, int a_cbf=-1)
This function perform the image update step specific to the optimizer.
int GetNbRespBasisFunctions()
Get the number of respiratory basis functions.
oImageConvolverManager * mp_ImageConvolverManager
int SensitivitySpecificOperations(FLTNB a_data, FLTNB a_forwardModel, FLTNB *ap_weight, FLTNB a_multiplicativeCorrections, FLTNB a_additiveCorrections, FLTNB a_blankValue, FLTNB a_quantificationFactor, oProjectionLine *ap_Line)
This function compute the weight associated to the provided event (for sensitivity computation) ...
virtual FLTNB ComputePenaltyValue(int a_tbf, int a_rbf, int a_cbf, INTNB a_voxel, int a_th)=0