/** Write file containing only cut atoms and energies as charges. */ int Action_Pairwise::WriteCutFrame(int frameNum, Topology const& Parm, AtomMask const& CutMask, Darray const& CutCharges, Frame const& frame, std::string const& outfilename) { if (CutMask.Nselected() != (int)CutCharges.size()) { mprinterr("Error: WriteCutFrame: # of charges (%u) != # mask atoms (%i)\n", CutCharges.size(), CutMask.Nselected()); return 1; } Frame CutFrame(frame, CutMask); Topology* CutParm = Parm.modifyStateByMask( CutMask ); if (CutParm == 0) return 1; // Set new charges for (int i = 0; i != CutParm->Natom(); i++) CutParm->SetAtom(i).SetCharge( CutCharges[i] ); int err = 0; Trajout_Single tout; if (tout.PrepareTrajWrite(outfilename, "multi", CutParm, CoordinateInfo(), 1, TrajectoryFile::MOL2FILE)) { mprinterr("Error: Could not set up cut mol2 file %s\n", outfilename.c_str()); err = 1; } else { tout.WriteSingle(frameNum, CutFrame); tout.EndTraj(); } delete CutParm; return err; }
Darray<float> cudot (const Darray<float>& lhs, const Darray<float>& rhs) { // context check CHECK_EQ(lhs.getDeviceManager().getDeviceID(), rhs.getDeviceManager().getDeviceID()); CHECK_EQ(lhs.ndim(), rhs.ndim()); CHECK_LT(lhs.ndim(), 3); CHECK_LT(rhs.ndim(), 3); Darray<float> ret; if (lhs.ndim()==1 && rhs.ndim()==1) { // shape check CHECK_EQ(lhs.size(), rhs.size()); ret = Darray<float>(lhs.getDeviceManager(), {1}); // using cublas sdot lhs.deviceSet(); cublasSdot (DeviceManager::handle, lhs.size(), lhs.data, 1, rhs.data, 1, ret.data); } // 2D matrix dot else if (lhs.ndim()==2 && rhs.ndim()==2) { // shape check CHECK_EQ(lhs.shape()[1], rhs.shape()[0]); ret = Darray<float>(lhs.getDeviceManager(), {lhs.shape()[0], rhs.shape()[1]}); // using cublas sgemm lhs.deviceSet(); const float alpha = 1.; const float beta = 0.; CUBLAS_SAFE_CALL( cublasSgemm (DeviceManager::handle, CUBLAS_OP_N, CUBLAS_OP_N, lhs.shape()[0], rhs.shape()[1], lhs.shape()[1], &alpha, lhs.dev_data, lhs.shape()[0], rhs.dev_data, rhs.shape()[0], &beta, ret.dev_data, ret.shape()[0]) ); } return ret; }
// CurveFit::CalcMeanStdev() void CurveFit::CalcMeanStdev(Darray const& Vals, double& mean, double& stdev) const { mean = 0.0; for (Darray::const_iterator val = Vals.begin(); val != Vals.end(); ++val) mean += *val; mean /= (double)Vals.size(); stdev = 0.0; if (Vals.size() > 1) { for (Darray::const_iterator val = Vals.begin(); val != Vals.end(); ++val) { double diff = mean - *val; stdev += (diff * diff); } stdev = sqrt( stdev / (double)(Vals.size() - 1) ); } }
// CurveFit::ParametersHaveProblems() bool CurveFit::ParametersHaveProblems(Darray const& Xvals_, Darray const& Yvals_, Darray const& ParamsIn) { if (ParamsIn.empty() || Xvals_.empty() || Yvals_.empty()) { errorMessage_ = "Parameters or coordinates are empty."; return true; } if (Xvals_.size() != Yvals_.size()) { errorMessage_ = "Number of X values != number of Y values."; return true; } if ( Xvals_.size() < ParamsIn.size() ) { errorMessage_ = "Number of parameters cannot be greater than number of XY values."; return true; } if (hasBounds_.empty()) hasBounds_.assign(ParamsIn.size(), false); else { if (hasBounds_.size() != ParamsIn.size() || Ubound_.size() != ParamsIn.size() || Lbound_.size() != ParamsIn.size()) { errorMessage_ = "Number of bounds does not match number of parameters."; return true; } for (dsize in = 0; in != ParamsIn.size(); in++) { if (hasBounds_[in]) { if ( Lbound_[in] >= Ubound_[in] ) { errorMessage_ = "Lower bound must be less than upper bound."; return true; } if (ParamsIn[in] <= Lbound_[in] || ParamsIn[in] >= Ubound_[in]) { errorMessage_ = "Initial parameter not within bounds."; return true; } } } } if (!Weights_.empty() && Weights_.size() != Xvals_.size()) { errorMessage_ = "Number of weights does not match number of XY values."; return true; } errorMessage_ = 0; return false; }
/** For final Y values vs given (presumably initial) Y values, calculate * correlation coefficient, chi-squared, Theil's U, and RMS percent * error. */ int CurveFit::Statistics(Darray const& Yvals, double& corr, double& ChiSq, double& TheilU, double& rms_percent_error) const { if (finalY_.empty() || Yvals.size() != finalY_.size()) return 9; int err_val = 0; unsigned int Nvals = Yvals.size(); // Correlation coefficient corr = 0.0; if (Nvals > 1) { double mean0, mean1, stdev0, stdev1; CalcMeanStdev(finalY_, mean0, stdev0); CalcMeanStdev(Yvals, mean1, stdev1); if (stdev0 > 0.0 && stdev1 > 0.0) { for (unsigned int n = 0; n != Nvals; n++) corr += (finalY_[n] - mean0) * (Yvals[n] - mean1); corr /= ((double)(Nvals - 1) * stdev0 * stdev1); } } ChiSq = 0.0; double Y2 = 0.0; bool setHasZero = false; for (unsigned int n = 0; n != Nvals; n++) { double diff = finalY_[n] - Yvals[n]; ChiSq += (diff * diff); Y2 += (Yvals[n] * Yvals[n]); if (Yvals[n] == 0.0) setHasZero = true; } TheilU = sqrt(ChiSq / Y2); rms_percent_error = 0.0; if (!setHasZero) { for (unsigned int n = 0; n != Nvals; n++) { double diff = finalY_[n] - Yvals[n]; rms_percent_error += (diff * diff) / (Yvals[n] * Yvals[n]); } rms_percent_error = sqrt( rms_percent_error / (double)Nvals ); } else err_val = 10; return err_val; }
float cunorm2 (const Darray<float>& ary) { ary.deviceSet(); float ret; CUBLAS_SAFE_CALL( cublasSnrm2 (DeviceManager::handle, ary.size(), ary.dev_data, 1, &ret) ); return ret; }
double cunorm2 (const Darray<double>& ary) { ary.deviceSet(); double ret; CUBLAS_SAFE_CALL( cublasDnrm2 (DeviceManager::handle, ary.size(), ary.dev_data, 1, &ret) ); return ret; }
// Analysis_TI::Calc_Increment() int Analysis_TI::Calc_Increment() { // Determine max points if not given. int maxpts = avg_max_; if (maxpts == -1) { for (unsigned int idx = 0; idx != input_dsets_.size(); idx++) { DataSet_1D const& ds = static_cast<DataSet_1D const&>( *(input_dsets_[idx]) ); if (maxpts == -1) maxpts = (int)ds.Size(); else if (maxpts != (int)ds.Size()) { mprintf("Warning: # points in '%s' (%zu) is different than %i.\n", ds.legend(), ds.Size(), maxpts); maxpts = std::min( maxpts, (int)ds.Size() ); mprintf("Warning: Will only use %i points.\n", maxpts); } } } if (maxpts < 1) { mprinterr("Error: Max points to use is < 1.\n"); return 1; } if (avg_skip_ >= maxpts) { mprinterr("Error: 'avgskip' (%i) > max (%i).\n", avg_skip_, maxpts); return 1; } // sum: Hold the results of integration for each curve (increment) Darray sum; // points: Hold point values at which each avg is being calculated Iarray points; // Loop over input data sets. for (unsigned int idx = 0; idx != input_dsets_.size(); idx++) { DataSet_1D const& ds = static_cast<DataSet_1D const&>( *(input_dsets_[idx]) ); if (CheckSet(ds)) return 1; // Calculate averages for each increment Darray avg; Iarray increments; int count = 0; int endpt = maxpts -1; double currentSum = 0.0; if (debug_ > 0) mprintf("DEBUG: Lambda %g\n", xval_[idx]); for (int pt = avg_skip_; pt != maxpts; pt++) { currentSum += ds.Dval(pt); count++; if (count == avg_increment_ || pt == endpt) { avg.push_back( currentSum / ((double)(pt - avg_skip_ + 1)) ); increments.push_back(pt+1); if (debug_ > 0) mprintf("DEBUG:\t\tAvg from %i to %i: %g\n", avg_skip_+1, pt+1, avg.back()); count = 0; } } if (sum.empty()) { sum.resize(avg.size()); points = increments; } else if (sum.size() != avg.size()) { mprinterr("Error: Different # of increments for set '%s'; got %zu, expected %zu.\n", ds.legend(), avg.size(), sum.size()); return 1; } // Create increment curve data sets if (curve_.empty()) { MetaData md(dAout_->Meta().Name(), "TIcurve"); for (unsigned int j = 0; j != avg.size(); j++) { md.SetIdx( increments[j] ); DataSet* ds = masterDSL_->AddSet(DataSet::XYMESH, md); if (ds == 0) return Analysis::ERR; ds->ModifyDim(Dimension::X).SetLabel("Lambda"); ds->SetLegend( md.Name() + "_Skip" + integerToString(increments[j]) ); if (curveout_ != 0) curveout_->AddDataSet( ds ); curve_.push_back( ds ); } } for (unsigned int j = 0; j != avg.size(); j++) { DataSet_Mesh& CR = static_cast<DataSet_Mesh&>( *(curve_[j]) ); CR.AddXY(xval_[idx], avg[j]); if (mode_ == GAUSSIAN_QUAD) sum[j] += (wgt_[idx] * avg[j]); } } // END loop over data sets if (mode_ == TRAPEZOID) Integrate_Trapezoid(sum); // Store final integration values DataSet_Mesh& DA = static_cast<DataSet_Mesh&>( *dAout_ ); DA.ModifyDim(Dimension::X).SetLabel("Point"); for (unsigned int j = 0; j != points.size(); j++) DA.AddXY(points[j], sum[j]); return 0; }
/** Given an array of already set up data sets (from e.g. input data files) * and optional X values, add the sets to this list if they do not exist * or append any existing sets. */ int DataSetList::AddOrAppendSets(std::string const& XlabelIn, Darray const& Xvals, DataListType const& Sets) { if (debug_ > 0) mprintf("DEBUG: Calling AddOrAppendSets for %zu sets, %zu X values, Xlabel= %s.\n", Sets.size(), Xvals.size(), XlabelIn.c_str()); if (Sets.empty()) return 0; // No error for now. // If no X label assume 'Frame' for backwards compat. std::string Xlabel; if (XlabelIn.empty()) Xlabel.assign("Frame"); else Xlabel = XlabelIn; Dimension Xdim; // First determine if X values increase monotonically with a regular step bool isMonotonic = true; double xstep = 1.0; if (Xvals.size() > 1) { xstep = (Xvals.back() - Xvals.front()) / (double)(Xvals.size() - 1); for (Darray::const_iterator X = Xvals.begin()+2; X != Xvals.end(); ++X) if ((*X - *(X-1)) - xstep > Constants::SMALL) { isMonotonic = false; break; } // Set dim even for non-monotonic sets so Label is correct. FIXME is this ok? Xdim = Dimension( Xvals.front(), xstep, Xlabel ); } else // No X values. set generic X dim. Xdim = Dimension(1.0, 1.0, Xlabel); if (debug_ > 0) { mprintf("DEBUG: xstep %g xmin %g\n", Xdim.Step(), Xdim.Min()); if (isMonotonic) mprintf("DEBUG: Xdim is monotonic.\n"); } for (const_iterator ds = Sets.begin(); ds != Sets.end(); ++ds) { if (*ds == 0) continue; // TODO: Warn about blanks? if (debug_ > 0) mprintf("DEBUG: AddOrAppend set '%s'", (*ds)->legend()); if (isMonotonic) (*ds)->SetDim(Dimension::X, Xdim); DataSet* existingSet = CheckForSet( (*ds)->Meta() ); if (existingSet == 0) { // New set. If set is scalar 1D but X values are not monotonic, // convert to XY mesh if necessary. if ( !isMonotonic && (*ds)->Group() == DataSet::SCALAR_1D && (*ds)->Type() != DataSet::XYMESH ) { DataSet* xyptr = AddSet( DataSet::XYMESH, (*ds)->Meta() ); if (xyptr == 0) { mprinterr("Error: Could not convert set %s to XY mesh.\n", (*ds)->legend()); continue; // TODO exit instead? } if ( (*ds)->Size() != Xvals.size() ) { // Sanity check mprinterr("Error: # of X values does not match set %s size.\n", (*ds)->legend()); continue; } DataSet_1D const& set = static_cast<DataSet_1D const&>( *(*ds) ); DataSet_Mesh& xy = static_cast<DataSet_Mesh&>( *xyptr ); for (unsigned int i = 0; i != set.Size(); i++) xy.AddXY( Xvals[i], set.Dval(i) ); xy.SetDim(Dimension::X, Xdim); if (debug_ > 0) mprintf(", New set, converted to XY-MESH\n"); // Free memory since set has now been converted. delete *ds; } else { // No conversion. Just add. (*ds)->SetDim(Dimension::X, Xdim); AddSet( *ds ); if (debug_ > 0) mprintf(", New set\n"); } } else { if (debug_ > 0) mprintf(", appending to existing set\n"); // Set exists. Try to append this set to existing set. bool canAppend = true; if ( (*ds)->Group() == DataSet::GENERIC ) { // For GENERIC sets, base Type must match. // TODO: Should this logic be in DataSets? if ( (*ds)->Type() != existingSet->Type() ) canAppend = false; } else { // For non-GENERIC sets, Group must match if ( (*ds)->Group() != existingSet->Group() ) canAppend = false; } if (!canAppend) mprinterr("Error: Cannot append set of type %s to set of type %s\n", DataArray[(*ds)->Type()].Description, DataArray[existingSet->Type()].Description); // If cannot append or attempt to append fails, rename and add as new set. if (!canAppend || existingSet->Append( *ds )) { // TODO Dimension check? if (canAppend) mprintf("Warning: Append currently not supported for type %s\n", DataArray[existingSet->Type()].Description); MetaData md = (*ds)->Meta(); md.SetName( GenerateDefaultName("X") ); mprintf("Warning: Renaming %s to %s\n", (*ds)->Meta().PrintName().c_str(), md.PrintName().c_str()); (*ds)->SetMeta( md ); AddSet( *ds ); } else // Set was appended to existing set; memory can be freed. delete *ds; } } // END loop over input sets return 0; }
// Exec_SortEnsembleData::Sort_pH_Data() int Exec_SortEnsembleData::Sort_pH_Data(DataSetList const& setsToSort, DataSetList& OutputSets, unsigned int maxFrames) const { // Cast sets back to DataSet_PHREMD typedef std::vector<DataSet_PHREMD*> Parray; Parray PHsets; for (DataSetList::const_iterator ds = setsToSort.begin(); ds != setsToSort.end(); ++ds) PHsets.push_back( (DataSet_PHREMD*)*ds ); // Gather initial pH data values, ensure no duplicates typedef std::vector<double> Darray; Darray pHvalues; # ifdef MPI pHvalues.resize( Parallel::Ensemble_Size() ); Darray phtmp; for (Parray::const_iterator ds = PHsets.begin(); ds != PHsets.end(); ++ds) phtmp.push_back( (*ds)->Initial_pH() ); if (comm_.AllGather(&phtmp[0], phtmp.size(), MPI_DOUBLE, &pHvalues[0])) { rprinterr("Error: Gathering pH values.\n"); return 1; } # else for (Parray::const_iterator ds = PHsets.begin(); ds != PHsets.end(); ++ds) pHvalues.push_back( (*ds)->Initial_pH() ); # endif ReplicaInfo::Map<double> pH_map; if (pH_map.CreateMap( pHvalues )) { rprinterr("Error: Duplicate pH value detected (%.2f) in ensemble.\n", pH_map.Duplicate()); return 1; } Darray sortedPH; mprintf("\tInitial pH values:"); for (ReplicaInfo::Map<double>::const_iterator ph = pH_map.begin(); ph != pH_map.end(); ++ph) { mprintf(" %6.2f", ph->first); sortedPH.push_back( ph->first ); } mprintf("\n"); // Create sets to hold sorted pH values. Create a set for each pH value // and each residue. Final output sets will be PH0R0, PH0R1, PH1R0, ... // TODO check that residue info all the same DataSet_PHREMD::Rarray const& Residues = PHsets[0]->Residues(); int defaultState = 0; # ifdef MPI if ( PHsets[0]->Type() == DataSet::PH_IMPL) defaultState = -1; # endif if (debug_ > 0) rprintf("DEBUG: Sorting %u frames for %zu sets, %zu pH values.\n", maxFrames, PHsets.size(), sortedPH.size()); for (unsigned int idx = 0; idx != sortedPH.size(); idx++) { OutputSets.SetEnsembleNum( idx ); for (unsigned int res = 0; res != Residues.size(); ++res) { MetaData md(PHsets[0]->Meta().Name(), Residues[res].Name().Truncated(), Residues[res].Num()); DataSet_pH* out = (DataSet_pH*)OutputSets.AddSet( DataSet::PH, md ); if (out==0) return 1; //out->SetLegend( "pH " + doubleToString( sortedPH[idx] ) ); out->Set_Solvent_pH( sortedPH[idx] ); out->SetResidueInfo( Residues[res] ); out->SetTimeValues(PHsets[0]->Time()); out->Resize( maxFrames, defaultState ); } } // --------------------------------------------- if ( PHsets[0]->Type() == DataSet::PH_EXPL) { // Loop over unsorted sets for (Parray::const_iterator ds = PHsets.begin(); ds != PHsets.end(); ++ds) { DataSet_PHREMD_Explicit* in = (DataSet_PHREMD_Explicit*)*ds; unsigned int phidx = 0; for (unsigned int n = 0; n < maxFrames; n++) { float phval = in->pH_Values()[n]; int setidx = pH_map.FindIndex( phval ) * Residues.size(); //rprintf("DEBUG: %6u Set %10s pH= %6.2f going to %2i\n", n+1, in->legend(), phval, idx); //mflush(); for (unsigned int res = 0; res < in->Residues().size(); res++, setidx++, phidx++) { DataSet_pH* out = (DataSet_pH*)OutputSets[setidx]; //if (res == 0 && idx == 0) { // rprintf("DEBUG: Frame %3u res %2u State %2i pH %6.2f\n", // n, res, in->Res(res).State(n), phval); // mflush(); //} out->SetState(n, in->ResStates()[phidx], in->RecordType(n)); } } } // END loop over unsorted sets # ifdef MPI // Now we need to reduce down each set onto the thread where it belongs. if (Parallel::World().Size() > 1) { for (int idx = 0; idx != (int)OutputSets.size(); idx++) { DataSet_pH* out = (DataSet_pH*)OutputSets[idx]; int ensembleRank = Parallel::MemberEnsCommRank( out->Meta().EnsembleNum() ); //rprintf("DEBUG: Reduce set %s to rank %i\n", out->legend(), ensembleRank); out->Reduce( comm_, ensembleRank ); } // Remove sets that do not belong on this rank for (int idx = (int)OutputSets.size() - 1; idx > -1; idx--) { DataSet* out = OutputSets[idx]; int ensembleRank = Parallel::MemberEnsCommRank( out->Meta().EnsembleNum() ); if (ensembleRank != comm_.Rank()) { //rprintf("DEBUG: Remove set %s (%i) from rank %i\n", out->legend(), // idx, comm_.Rank()); OutputSets.RemoveSet( out ); } } } # endif // --------------------------------------------- } else if ( PHsets[0]->Type() == DataSet::PH_IMPL) { # ifdef MPI typedef std::vector<int> Iarray; typedef std::vector<Iarray> Iarray2; typedef std::vector<bool> Barray; // True if I have this pH value Barray isMyPh( sortedPH.size(), false ); // Which rank in ensemble has which pH Iarray pHrank( sortedPH.size(), 0 ); for (int phidx = 0; phidx != (int)sortedPH.size(); phidx++) { int ensembleRank = Parallel::MemberEnsCommRank( phidx ); pHrank[phidx] = ensembleRank; isMyPh[phidx] = (ensembleRank == comm_.Rank()); } // DEBUG for (unsigned int idx = 0; idx != pHrank.size(); idx++) mprintf("\tpH %6.2f on rank %i\n", sortedPH[idx], pHrank[idx]); // Hold frame-residue-state for each pH not on this rank. Iarray2 FrmResState( sortedPH.size() ); // Loop over unsorted sets for (Parray::const_iterator ds = PHsets.begin(); ds != PHsets.end(); ++ds) { DataSet_PHREMD_Implicit* in = (DataSet_PHREMD_Implicit*)*ds; // Loop over frames for (unsigned int n = 0; n < maxFrames; n++) { DataSet_PHREMD_Implicit::Record const& Rec = in->Records()[n]; float phval = Rec.pH(); int phidx = pH_map.FindIndex( phval ); if (isMyPh[phidx]) { // This pH belongs to me. Set value. int setidx = phidx * Residues.size(); if (Rec.RecType() == Cph::FULL_RECORD) { // Info for all residues for (unsigned int res = 0; res < in->Residues().size(); res++, setidx++) ((DataSet_pH*)OutputSets[setidx])->SetState(n, Rec.ResStates()[res], Rec.RecType()); } else if (Rec.RecType() > -1) { // Info for single residue, record type for all residues //rprintf("\tSetting my pH %6.2f frame %8i state %2i idx %6i res %6i '%s'\n", sortedPH[phidx], n, Rec.ResStates()[0], setidx, Rec.RecType(), OutputSets[setidx+Rec.RecType()]->legend()); for (int res = 0; res < (int)in->Residues().size(); res++, setidx++) if (res == Rec.RecType()) ((DataSet_pH*)OutputSets[setidx])->SetState(n, Rec.ResStates()[0], Rec.RecType()); else ((DataSet_pH*)OutputSets[setidx])->SetRecType(n, Rec.RecType()); } } else { // This pH belongs to another rank. Save it. if (Rec.RecType() > -1) { // Info for a single residue present FrmResState[phidx].push_back( n ); FrmResState[phidx].push_back( Rec.RecType() ); FrmResState[phidx].push_back( Rec.ResStates()[0] ); } else { // Info for all residues present FrmResState[phidx].push_back( n ); FrmResState[phidx].push_back( Rec.RecType() ); for (unsigned int res = 0; res < in->Residues().size(); res++) FrmResState[phidx].push_back( Rec.ResStates()[res] ); } } } // END loop over frames } // END loop over sets // DEBUG /* comm_.Barrier(); for (int rank = 0; rank < comm_.Size(); rank++) { if (rank == comm_.Rank()) { for (unsigned int phidx = 0; phidx != sortedPH.size(); phidx++) { rprintf("DEBUG: pH %6.2f: %8s %6s %2s\n", sortedPH[phidx], "Frm", "Res", "St"); Iarray const& FRS = FrmResState[phidx]; unsigned int idx = 0; while (idx < FRS.size()) { int rec = FRS[idx+1]; if (rec > -1) { rprintf(" %8i %6i %2i\n", FRS[idx], rec, FRS[idx+2]); idx += 3; } else { rprintf(" %8i %6i All Residues\n", FRS[idx], rec); idx += (2 + Residues.size()); } } } } comm_.Barrier(); } */ // Communicate states to other ranks typedef std::vector<unsigned int> Uarray; Uarray sizeOnRank( comm_.Size() ); for (unsigned int phidx = 0; phidx != sortedPH.size(); phidx++) { // Each rank says how many frames of this pH they have collected and // send to rank the pH belongs to. unsigned int nph = FrmResState[phidx].size(); comm_.Gather(&nph, 1, MPI_UNSIGNED, &sizeOnRank[0], pHrank[phidx]); if (pHrank[phidx] == comm_.Rank()) { // This pH belongs to me. I should have no frames at this pH. if (sizeOnRank[comm_.Rank()] > 0) { rprinterr("Internal Error: Rank has frames to communicate at its pH\n"); Parallel::Abort(1); } unsigned int totalSize = 0; for (unsigned int idx = 0; idx != sizeOnRank.size(); idx++) { totalSize += sizeOnRank[idx]; //rprintf("DEBUG: Rank %4u has %8u frames of pH %6.2f\n", // idx, sizeOnRank[idx], sortedPH[phidx]); } //rprintf("DEBUG: Total incoming size: %u\n", totalSize); FrmResState[phidx].resize( totalSize ); // Receive state info for this pH from other ranks int* frsArray = &(FrmResState[phidx][0]); for (int rank = 0; rank != comm_.Size(); rank++) { if (rank != comm_.Rank()) { comm_.Recv(frsArray, sizeOnRank[rank], MPI_INT, rank, 1600+rank); frsArray += sizeOnRank[rank]; } } } else { // This pH belongs to another rank. Send my info there. int* frsArray = &(FrmResState[phidx][0]); comm_.Send(frsArray, nph, MPI_INT, pHrank[phidx], 1600+comm_.Rank()); } comm_.Barrier(); } // Fill in state info std::vector<DataSet*> ToRemove; for (unsigned int phidx = 0; phidx != sortedPH.size(); phidx++) { int setidx = phidx * Residues.size(); if (pHrank[phidx] == comm_.Rank()) { Iarray const& FRS = FrmResState[phidx]; // This pH belongs to me. Fill in the information received from // other ranks. unsigned int idx = 0; while (idx < FRS.size()) { int rec = FRS[idx+1]; if (rec > -1) { // Info for single residue, record type for all residues //rprintf("\tSetting pH %6.2f frame %8i state %2i idx %6i res %6i '%s'\n", sortedPH[phidx], FRS[idx], FRS[idx+2], setidx, rec, OutputSets[setidx+rec]->legend()); for (int res = 0; res != (int)Residues.size(); res++) { DataSet_pH* out = (DataSet_pH*)OutputSets[setidx + res]; if (rec == res) out->SetState( FRS[idx], FRS[idx+2], rec ); else out->SetRecType( FRS[idx], rec ); } idx += 3; } else { //rprintf(" %8i %6i All Residues\n", FRS[idx], rec); int frm = FRS[idx]; idx += 2; for (unsigned int res = 0; res != Residues.size(); res++, idx++) { DataSet_pH* out = (DataSet_pH*)OutputSets[setidx + res]; out->SetState( frm, FRS[idx], rec ); } } } // Fill in any remaining data. FIXME safe to assume first frame is set? for (unsigned int res = 0; res != Residues.size(); res++) { DataSet_pH* out = (DataSet_pH*)OutputSets[setidx + res]; for (unsigned int n = 1; n < maxFrames; n++) if (out->State(n) == -1) out->SetState(n, out->State(n-1), out->RecordType(n)); } } else { // This pH does not belong to me. Mark associated data sets to be removed. for (unsigned int res = 0; res != Residues.size(); res++) ToRemove.push_back( OutputSets[setidx + res] ); } } // Remove data sets that do not belong to me. for (std::vector<DataSet*>::reverse_iterator it = ToRemove.rbegin(); it != ToRemove.rend(); ++it) { //rprintf("DEBUG: '%s' does not belong to me.\n", (*it)->legend()); OutputSets.RemoveSet( *it ); } # else /* if not MPI */ // Loop over frames for (unsigned int n = 0; n < maxFrames; n++) { // Loop over unsorted sets for (Parray::const_iterator ds = PHsets.begin(); ds != PHsets.end(); ++ds) { DataSet_PHREMD_Implicit* in = (DataSet_PHREMD_Implicit*)*ds; DataSet_PHREMD_Implicit::Record const& Rec = in->Records()[n]; float phval = Rec.pH(); int setidx = pH_map.FindIndex( phval ) * Residues.size(); if (Rec.RecType() == Cph::FULL_RECORD) { for (unsigned int res = 0; res < in->Residues().size(); res++, setidx++) { DataSet_pH* out = (DataSet_pH*)OutputSets[setidx]; //if (res == 0 && idx == 0) { // rprintf("DEBUG: Frame %3u res %2u State %2i pH %6.2f\n", // n, res, in->Res(res).State(n), phval); // mflush(); //} out->SetState(n, Rec.ResStates()[res], Rec.RecType()); } } else { for (int res = 0; res < (int)in->Residues().size(); res++, setidx++) { DataSet_pH* out = (DataSet_pH*)OutputSets[setidx]; if (res == Rec.RecType()) out->SetState(n, Rec.ResStates()[0], Rec.RecType()); else // State for this residue not recorded - use previous state. // Should be fine since first state always has all residues. out->SetState(n, out->State(n-1), Rec.RecType()); } } } // END loop over unsorted sets } // END loop over frames # endif /* MPI */ // --------------------------------------------- } else { return 1; // Sanity check } return 0; }
/** Perform curve fitting via Levenberg-Marquardt method with optional bounds. * \param fxnIn Function to fit. * \param Xvals_ target X values (ordinates, M) * \param Yvals_ target Y values (coordinates, M) * \param ParamVec input parameters(N); at finish contains best esimate of fit parameters * \param boundsIn true if parameter has bounds (N) * \param lboundIn contain lower bounds for parameter (N) * \param uboundIn contain upper bounds for parameter (N) * \param weightsIn contain weights for Y values (M) * \param tolerance Fit tolerance * \param maxIterations Number of iterations to try. */ int CurveFit::LevenbergMarquardt(FitFunctionType fxnIn, Darray const& Xvals_, Darray const& Yvals_, Darray& ParamVec, std::vector<bool> const& boundsIn, Darray const& lboundIn, Darray const& uboundIn, Darray const& weightsIn, double tolerance, int maxIterations) { int info = 0; hasBounds_ = boundsIn; Ubound_ = uboundIn; Lbound_ = lboundIn; Weights_ = weightsIn; if (ParametersHaveProblems(Xvals_, Yvals_, ParamVec)) { DBGPRINT("Error: %s\n", errorMessage_); return info; } // Set initial parameters finalY_ = Yvals_; Params_= ParamVec; // Internal parameter vector fParms_ = ParamVec; // Parameters for function evaluation Pvec_to_Params( ParamVec ); fxn_ = fxnIn; m_ = Xvals_.size(); // Number of values (rows) n_ = Params_.size(); // Number of parameters (cols) Darray residual_( m_ ); // Contain Y vals at current Params minus original Y // Jacobian matrix/R: m rows by n cols, transposed. jacobian_.assign( m_ * n_, 0.0 ); // For holding diagonal elements for scaling // TODO: Rename Darray diag( n_, 0.0 ); // Workspace arrays Darray work1( n_, 0.0 ); Darray work2( n_, 0.0 ); Darray newResidual( m_, 0.0 ); // delta specifies an upper bound on the euclidean norm of d*x double delta = 0.0; // factor is positive input variable used in determining the initial step // bound. This bound is set to the product of factor and the euclidean norm // of diag*x if nonzero, or else to factor itself. In most cases factor should // lie in the interval (.1,100.). 100. is a generally recommended value. // TODO: Make input parameter const double factor = 100.0; // gtol is a nonnegative input variable. Termination occurs when the cosine // of the angle between residual and any column of the Jacobian is at most // gtol in absolute value. Therefore, gtol measures the orthogonality // desired between the function vector and the columns of the Jacobian. // TODO: Make input parameter const double gtol = 0.0; // Smallest possible magnitude // TODO: Make Constant? const double dwarf = DBL_MIN; // Levenberg-Marquard parameter double LM_par = 0.0; // xtol is a nonnegative input variable. Termination occurs when the // relative error between two consecutive iterates is at most xtol. // Therefore, xtol measures the relative error desired in the // approximate solution. // TODO: Make input paramter double xtol = 0.0; // maxfev is the maxiumum number of allowed function evaluations. // NOTE: Included here for complete compatibility with eariler code, // though may not be strictly necessary. dsize maxfev = (n_ + 1) * (dsize)maxIterations; // Evaluate initial function, obtain residual EvaluateFxn( Xvals_, Yvals_, Params_, residual_ ); // Calculate norm of the residual double rnorm = VecNorm( residual_ ); DBGPRINT("Rnorm= %g\n", rnorm); dsize nfev = 1; // Will be set to calls to fxn_. 1 already. // MAIN LOOP int currentIt = 0; while ( currentIt < maxIterations ) { DBGPRINT("DEBUG: ----- Iteration %i ------------------------------\n", currentIt+1); // Calculate the Jacobian using the forward-difference approximation. CalcJacobian_ForwardDiff( Xvals_, Yvals_, Params_, residual_, newResidual ); PrintMatrix( "Jacobian", n_, m_, jacobian_ ); nfev += n_; // ------------------------------------------- // | BEGIN QRFAC // ------------------------------------------- // Perform QR factorization of Jacobian via Householder transformations // with column pivoting: // J * P = Q * R // where J is the Jacobian, P is the permutation matrix, Q is an orthogonal // matrix, and R is an upper trapezoidal matrix with diagonal elements of // nonincreasing magnitude. The Householder transformation for column k, // k = 1,2,...,min(m,n), is of the form: // I = (1/u[k])*u*ut // where u has zeros in the first k-1 positions. Adapted from routine qrfac_ // in Grace 5.1.22 lmdif.c, which in turn is derived from an earlier linpack // subroutine. DBGPRINT("\nQRFAC ITERATION %i\n", currentIt+1); // Rdiag will hold the diagonal elements of R. Darray Rdiag(n_, 0.0); // Jpvt defines the permutation matrix p such that a*p = q*r. Column j of p // is column Jpvt[j] of the identity matrix. Iarray Jpvt(n_, 0.0); // JcolNorm Will hold the norms of the columns of input Jacobian. Darray JcolNorm_(n_, 0.0); // Compute the initial column norms and initialize arrays. for (dsize in = 0; in != n_; in++) { JcolNorm_[in] = VecNorm( jacobian_.begin() + (in * m_), m_ ); Rdiag[in] = JcolNorm_[in]; work1[in] = Rdiag[in]; Jpvt[in] = in; DBGPRINT("%lu: Rdiag= %12.6g wa= %12.6g ipvt= %i\n", in+1, Rdiag[in], work1[in], Jpvt[in]+1); } // Reduce Jacobian to R with Householder transformations. dsize min_m_n = std::min( m_, n_ ); for (dsize in = 0; in != min_m_n; in++) { // Bring the column of largest norm into the pivot position. dsize kmax = in; for (dsize k = in; k != n_; k++) { DBGPRINT("\tRdiag[%lu]= %g Rdiag[%lu]= %g\n", k+1, Rdiag[k], kmax+1, Rdiag[kmax]); if (Rdiag[k] > Rdiag[kmax]) kmax = k; } DBGPRINT("Elt= %lu Kmax= %lu\n", in+1, kmax+1); if (kmax != in) { for (dsize i = 0; i != m_; i++) { double temp = jacobian_[i + in * m_]; jacobian_[i + in * m_] = jacobian_[i + kmax * m_]; jacobian_[i + kmax * m_] = temp; DBGPRINT("DBG: Swap jac[%lu,%lu] with jac[%lu,%lu]\n", i+1, in+1, i+1, kmax+1); } Rdiag[kmax] = Rdiag[in]; work1[kmax] = work1[in]; dsize k = Jpvt[in]; Jpvt[in] = Jpvt[kmax]; Jpvt[kmax] = k; } // Compute the Householder transformation to reduce the j-th column // of Jacobian to a multiple of the j-th unit vector. double ajnorm = VecNorm( jacobian_.begin() + (in + in * m_), m_ - in ); DBGPRINT("#Elt= %lu mat[%lu]= %g ajnorm= %g\n", m_ - in, in + in * m_, jacobian_[in + in * m_], ajnorm); if (ajnorm != 0.0) { if (jacobian_[in + in * m_] < 0.0) ajnorm = -ajnorm; for (dsize im = in; im < m_; im++) jacobian_[im + in * m_] /= ajnorm; jacobian_[in + in * m_] += 1.0; // Apply the transfomation to the remaining columns and update the norms dsize in1 = in + 1; if (in1 < n_) { for (dsize k = in1; k < n_; k++) { double sum = 0.0; for (dsize i = in; i < m_; i++) sum += jacobian_[i + in * m_] * jacobian_[i + k * m_]; double temp = sum / jacobian_[in + in * m_]; for (dsize i = in; i < m_; i++) jacobian_[i + k * m_] -= temp * jacobian_[i + in * m_]; if (Rdiag[k] != 0.0) { temp = jacobian_[in + k * m_] / Rdiag[k]; Rdiag[k] *= sqrt( std::max( 0.0, 1.0 - temp * temp ) ); temp = Rdiag[k] / work1[k]; DBGPRINT("\t\tQRFAC TEST: 0.5 * %g^2 <= %g\n", temp, machine_epsilon); if (0.05 * (temp * temp) <= machine_epsilon) { DBGPRINT("\t\tTEST PASSED\n"); Rdiag[k] = VecNorm( jacobian_.begin() + (in1 + k * m_), m_ - in - 1 ); work1[k] = Rdiag[k]; } } DBGPRINT("QRFAC Rdiag[%lu]= %g\n", k+1, Rdiag[k]); } } } Rdiag[in] = -ajnorm; } // ------------------------------------------- // | END QRFAC // ------------------------------------------- PrintMatrix("QR", n_, m_, jacobian_); for (dsize in = 0; in != n_; in++) DBGPRINT("\tRdiag[%lu]= %12.6g acnorm[%lu]= %12.6g ipvt[%lu]= %i\n", in+1, Rdiag[in], in+1, JcolNorm_[in], in+1, Jpvt[in]+1); double xnorm = 0.0; if ( currentIt == 0 ) { // First iteration. Scale according to the norms of the columns of // the initial Jacobian. for (dsize in = 0; in != n_; in++) { if ( JcolNorm_[in] == 0.0 ) diag[in] = 1.0; else diag[in] = JcolNorm_[in]; } PrintVector("diag", diag); // Calculate norm of scaled params and init step bound delta for (dsize in = 0; in != n_; in++) work1[in] = diag[in] * Params_[in]; xnorm = VecNorm( work1 ); delta = factor * xnorm; if (delta == 0.0) delta = factor; DBGPRINT("Delta= %g\n", delta); } // Form Qt * residual and store in Qt_r. Only first n components of // Qt*r are needed. Darray Qt_r( n_, 0.0 ); newResidual = residual_; for (dsize in = 0; in != n_; in++) { double matElt = jacobian_[in + in * m_]; DBGPRINT("DEBUG: Element %lu = %g\n", in+1, matElt); if (matElt != 0.0) { double sum = 0.0; for (dsize i = in; i < m_; i++) sum += jacobian_[i + in * m_] * newResidual[i]; double temp = -sum / matElt; for (dsize i = in; i < m_; i++) newResidual[i] += jacobian_[i + in * m_] * temp; } jacobian_[in + in * m_] = Rdiag[in]; DBGPRINT("\tRdiag[%lu]= %g\n", in+1, jacobian_[in + in * m_]); Qt_r[in] = newResidual[in]; DBGPRINT("DEBUG: qtf[%lu]= %g\n", in+1, Qt_r[in]); } // Compute the norm of the scaled gradient. double gnorm = 0.0; if ( rnorm > 0.0 ) { for (dsize in = 0; in != n_; in++) { int l = Jpvt[ in ]; if (JcolNorm_[in] != 0.0) { double sum = 0.0; for (dsize i2 = 0; i2 <= in; i2++) { sum += jacobian_[i2 + in * m_] * (Qt_r[i2] / rnorm); DBGPRINT("DEBUG: jacobian[%lu, %lu]= %g\n", i2+1, in+1, jacobian_[i2 + in * m_]); } // Determine max double d1 = fabs( sum / JcolNorm_[l] ); gnorm = std::max( gnorm, d1 ); } } } DBGPRINT("gnorm= %g\n", gnorm); // Test for convergence of gradient norm. if (gnorm <= gtol) { DBGPRINT("Gradient norm %g is less than gtol %g, iteration %i\n", gnorm, gtol, currentIt+1); info = 4; break; } // Rescale if necessary for (dsize in = 0; in != n_; in++) diag[in] = std::max( diag[in], JcolNorm_[in] ); PrintVector("RescaleDiag", diag); double Ratio = 0.0; while (Ratio < 0.0001) { // ----------------------------------------- // | LMPAR BEGIN // ----------------------------------------- // NOTE: Adapted from lmpar_ in lmdif.c from Grace 5.1.22 DBGPRINT("\nLMPAR ITERATION %i\n", currentIt+1); // Determine the Levenberg-Marquardt parameter. Darray Xvec(n_, 0.0); // Will contain solution to A*x=b, sqrt(par)*D*x=0 // Compute and store in Xvec the Gauss-Newton direction. // If the Jacobian is rank-deficient, obtain a least-squares solution. dsize rank = n_; for (dsize in = 0; in != n_; in++) { work1[in] = Qt_r[in]; DBGPRINT("jac[%lu,%lu]= %12.6g rank= %4lu", in+1, in+1, jacobian_[in + in * m_], rank); if (jacobian_[in + in * m_] == 0.0 && rank == n_) rank = in; if (rank < n_) work1[in] = 0.0; DBGPRINT(" wa1= %12.6g\n", work1[in]); } DBGPRINT("Final rank= %lu\n", rank); // Subtract 1 from rank to use as an index. long int rm1 = rank - 1; if (rm1 >= 0) { for (long int k = 0; k <= rm1; k++) { long int in = rm1 - k; work1[in] /= jacobian_[in + in * (long int)m_]; DBGPRINT("wa1[%li] /= %g\n", in+1, jacobian_[in + in * (long int)m_]); long int in1 = in - 1; if (in1 > -1) { double temp = work1[in]; for (long int i = 0; i <= in1; i++) { work1[i] -= jacobian_[i + in * (long int)m_] * temp; DBGPRINT(" wa1[%li] -= %g\n", i+1, jacobian_[i + in * (long int)m_]); } } } } PrintVector("work1", work1); for (dsize in = 0; in != n_; in++) Xvec[ Jpvt[in] ] = work1[in]; // Evaluate the function at the origin, and test for acceptance of // the Gauss-Newton direction. for (dsize in = 0; in != n_; in++) { DBGPRINT("work2[%lu] = %g * %g\n", in+1, diag[in], Xvec[in]); work2[in] = diag[in] * Xvec[in]; } double dxnorm = VecNorm( work2 ); DBGPRINT("dxnorm= %g\n", dxnorm); double fp = dxnorm - delta; // Initialize counter for searching for LM parameter int lmIterations = 0; if (fp > 0.1 * delta) { // If the Jacobian is not rank deficient, the Newton step provdes a lower // bound, parl, for the zero of the function. Otherwise set this bound to // zero double parl = 0.0; if ( rank >= n_ ) { for (dsize in = 0; in != n_; in++) { int idx = Jpvt[in]; work1[in] = diag[idx] * (work2[idx] / dxnorm); } for (dsize in = 0; in != n_; in++) { double sum = 0.0; long int in1 = (long int)in - 1; if (in1 > -1) { for (long int i = 0; i <= in1; i++) sum += jacobian_[i + in * m_] * work1[i]; } work1[in] = (work1[in] - sum) / jacobian_[in + in * m_]; } double temp = VecNorm(work1); parl = fp / delta / temp / temp; } DBGPRINT("parl= %g\n",parl); // Calculate an upper bound, paru, for the zero of the function for (dsize in = 0; in != n_; in++) { double sum = 0.0; for (dsize i = 0; i <= in; i++) sum += jacobian_[i + in * m_] * Qt_r[i]; work1[in] = sum / diag[ Jpvt[in] ]; DBGPRINT("paru work1[%lu]= %g\n", in+1, work1[in]); } double w1norm = VecNorm( work1 ); double paru = w1norm / delta; DBGPRINT("paru = %g = %g / %g\n", paru, w1norm, delta); if (paru == 0.0) paru = dwarf / std::min( delta, 0.1 ); DBGPRINT("paru= %g\n", paru); // If the current L-M parameter lies outside of the interval (parl,paru), // set par to the closer endpoint. LM_par = std::max( LM_par, parl ); LM_par = std::min( LM_par, paru ); if (LM_par == 0.0) LM_par = w1norm / dxnorm; // Iteration start. bool lmLoop = true; while (lmLoop) { ++lmIterations; DBGPRINT("\t[ lmLoop %i parl= %g paru= %g LM_par= %g ]\n", lmIterations, parl, paru, LM_par); // Evaluate the function at the current value of LM_par if (LM_par == 0.0) LM_par = std::max( dwarf, 0.001 * paru ); double temp = sqrt( LM_par ); for (dsize in = 0; in != n_; in++) { work1[in] = temp * diag[in]; DBGPRINT("\tLMPAR work1[%lu]= %g = %g * %g\n", in+1, work1[in], temp, diag[in]); } // ----------------------------------------------- // | BEGIN QRSOLV // ----------------------------------------------- // NOTE: Adapted from qrsolv_ in lmdif.c from Grace 5.1.22 DBGPRINT("\nQRSOLV ITERATION %i\n", lmIterations); // This array of length n will hold the diagonal elements of the // upper triangular matrix s. Darray Sdiag(n_, 0.0); // Copy R and Qt_r to preserve input and initialize s. for (dsize in = 0; in != n_; in++) { for (dsize i = in; i != n_; i++) jacobian_[i + in * m_] = jacobian_[in + i * m_]; Xvec[in] = jacobian_[in + in * m_]; work2[in] = Qt_r[in]; } // Eliminate the diagonal matrix D using a Givens rotation for (dsize in = 0; in != n_; in++) { // Prepare the row of D to be eliminated, locating the diagonal // element using p from the QR factorization. double diagL = work1[ Jpvt[in] ]; DBGPRINT("diag[%i] = %g\n", Jpvt[in]+1, diagL); if (diagL != 0.0) { for (dsize k = in; k < n_; k++) Sdiag[k] = 0.0; Sdiag[in] = diagL; // The transformations to eliminate the row of D modify only // a single element of Qt_r beyond the first n, which is // initially zero. double qtbpj = 0.0; for (dsize k = in; k < n_; k++) { // Determine a Givens rotation which eliminates the appropriate // element in the current row of D. if (Sdiag[k] != 0.0) { double d1 = jacobian_[k + k * m_]; double cos, sin; if (fabs(d1) < fabs(Sdiag[k])) { double cotan = jacobian_[k + k * m_] / Sdiag[k]; sin = 0.5 / sqrt(0.25 + 0.25 * (cotan * cotan)); cos = sin * cotan; } else { double tan = Sdiag[k] / jacobian_[k + k *m_]; cos = 0.5 / sqrt(0.25 + 0.25 * (tan * tan)); sin = cos * tan; } DBGPRINT("DBG QRsolv: %lu, %lu: cos= %12.6g sin= %12.6g\n", in+1, k+1, cos, sin); // Compute the modified diagonal element of R and the // modified element of (Qt_r, 0) jacobian_[k + k * m_] = cos * jacobian_[k + k * m_] + sin * Sdiag[k]; double temp = cos * work2[k] + sin * qtbpj; qtbpj = -sin * work2[k] + cos * qtbpj; work2[k] = temp; DBGPRINT("\twork2[%lu]= %g\n", k+1, work2[k]); // Accumulate the transformation in the row of S dsize kp1 = k + 1; if (kp1 <= n_) { for (dsize i = kp1; i < n_; i++) { temp = cos * jacobian_[i + k * m_] + sin * Sdiag[i]; Sdiag[i] = -sin * jacobian_[i + k * m_] + cos * Sdiag[i]; jacobian_[i + k * m_] = temp; } } } } } // Store the diagonal element of s and restore the corresponding // diagonal element of r Sdiag[in] = jacobian_[in + in * m_]; DBGPRINT("QRsolv sdiag[%lu]= %g\n", in+1, Sdiag[in]); jacobian_[in + in * m_] = Xvec[in]; } // Solve the triangular system for z. if the system is singular, // then obtain a least-squares solution. dsize u_nsing = n_; for (dsize in = 0; in != n_; in++) { if (Sdiag[in] == 0.0 && u_nsing == n_) u_nsing = in; if (u_nsing < n_) work2[in] = 0.0; } DBGPRINT("nsing= %lu\n", u_nsing); // Subtract 1 from nsing to use as an index. long int nsing = (long int)u_nsing - 1; if (nsing >= 0) { for (long int k = 0; k <= nsing; k++) { long int in = nsing - k; double sum = 0.0; long int in1 = in + 1; if (in1 <= nsing) { for (long int i = in1; i <= nsing; i++) sum += jacobian_[i + in * (long int)m_] * work2[i]; } work2[in] = (work2[in] - sum) / Sdiag[in]; } } // Permute the components of z back to components of x. for (dsize in = 0; in != n_; in++) Xvec[ Jpvt[in] ] = work2[in]; PrintVector("QRsolv Xvec", Xvec); PrintVector("sdiag", Sdiag); // ----------------------------------------------- // | END QRSOLV // ----------------------------------------------- for (dsize in = 0; in != n_; in++) { work2[in] = diag[in] * Xvec[in]; DBGPRINT("\twork2[%lu]= %g = %g * %g\n", in+1, work2[in], diag[in], Xvec[in]); } dxnorm = VecNorm( work2 ); temp = fp; fp = dxnorm - delta; DBGPRINT("DBG LMPAR: fp = %g = %g - %g\n", fp, dxnorm, delta); // If the function is small enough, accept the current value of LM_par. // Also test for the exceptional cases where parl is zero of the number // of iterations has reached 10. if (fabs(fp) <= 0.1 * delta || (parl == 0.0 && fp <= temp && temp < 0.0) || lmIterations == 10) { lmLoop = false; break; } // Compute the Newton correction. for (dsize in = 0; in != n_; in++) { int idx = Jpvt[ in ]; work1[in] = diag[idx] * (work2[idx] / dxnorm); } for (dsize in = 0; in != n_; in++) { work1[in] /= Sdiag[in]; temp = work1[in]; dsize in1 = in + 1; if (in1 <= n_) { for (dsize i = in1; i < n_; i++) work1[i] -= jacobian_[i + in * m_] * temp; } } temp = VecNorm( work1 ); double parc = fp / delta / temp / temp; DBGPRINT("parc= %g\n", parc); // Depending on the sign of the function, update parl or paru if (fp > 0.0) parl = std::max( parl, LM_par ); if (fp < 0.0) paru = std::min( paru, LM_par ); // Compute an improved estimate for the parameter LM_par = std::max( parl, LM_par + parc ); } } DBGPRINT("END LMPAR iter= %i LM_par= %g\n", lmIterations, LM_par); if (lmIterations == 0) LM_par = 0.0; // ------------------------------------------- // | LMPAR END // ------------------------------------------- DBGPRINT("DEBUG: LM_par is %g\n", LM_par); // Store the direction Xvec and Param + Xvec. Calculate norm of Param. PrintVector("DEBUG: hvec", Xvec); for (dsize in = 0; in != n_; in++) { Xvec[in] = -Xvec[in]; work1[in] = Params_[in] + Xvec[in]; work2[in] = diag[in] * Xvec[in]; } double pnorm = VecNorm( work2 ); DBGPRINT("pnorm= %g\n", pnorm); // On first iteration, adjust initial step bound if (currentIt == 0) delta = std::min( delta, pnorm ); DBGPRINT("Delta is now %g\n", delta); // Evaluate function at Param + Xvec and calculate its norm EvaluateFxn( Xvals_, Yvals_, work1, newResidual ); ++nfev; double rnorm1 = VecNorm( newResidual ); DBGPRINT("rnorm1= %g\n", rnorm1); // Compute the scaled actual reduction double actual_reduction = -1.0; if ( 0.1 * rnorm1 < rnorm) { double d1 = rnorm1 / rnorm; actual_reduction = 1.0 - d1 * d1; } DBGPRINT("actualReduction= %g\n", actual_reduction); // Compute the scaled predicted reduction and the scaled directional // derivative. for (dsize in = 0; in != n_; in++) { work2[in] = 0.0; double temp = Xvec[ Jpvt[in] ]; for (dsize i = 0; i <= in; i++) work2[i] += jacobian_[i + in * m_] * temp; } double temp1 = VecNorm( work2 ) / rnorm; double temp2 = sqrt(LM_par) * pnorm / rnorm; DBGPRINT("temp1= %g temp2= %g\n", temp1, temp2); double predicted_reduction = temp1 * temp1 + temp2 * temp2 / 0.5; double dirder = -(temp1 * temp1 + temp2 * temp2); DBGPRINT("predictedReduction = %12.6g dirder= %12.6g\n", predicted_reduction, dirder); // Compute the ratio of the actial to the predicted reduction. if (predicted_reduction != 0.0) Ratio = actual_reduction / predicted_reduction; DBGPRINT("ratio= %g\n", Ratio); // Update the step bound if (Ratio <= 0.25) { DBGPRINT("Ratio <= 0.25\n"); double temp; if (actual_reduction < 0.0) temp = 0.5 * dirder / (dirder + 0.5 * actual_reduction); else temp = 0.5; if ( 0.1 * rnorm1 >= rnorm || temp < 0.1 ) temp = 0.1; DBGPRINT("delta = %g * min( %g, %g )\n", temp, delta, pnorm / 0.1); delta = temp * std::min( delta, pnorm / 0.1 ); LM_par /= temp; } else { if (LM_par == 0.0 || Ratio >= 0.75) { DBGPRINT("Ratio > 0.25 and (LMpar is zero or Ratio >= 0.75\n"); delta = pnorm / 0.5; LM_par *= 0.5; } } DBGPRINT("LM_par= %g Delta= %g\n", LM_par, delta); if (Ratio >= 0.0001) { // Successful iteration. Update Param, residual, and their norms. for (dsize in = 0; in != n_; in++) { Params_[in] = work1[in]; work1[in] = diag[in] * Params_[in]; } for (dsize im = 0; im < m_; im++) residual_[im] = newResidual[im]; xnorm = VecNorm( work1 ); rnorm = rnorm1; } // Tests for convergence if (fabs(actual_reduction) <= tolerance && predicted_reduction <= tolerance && 0.5 * Ratio <= 1.0) info = 1; if (delta <= xtol * xnorm) info = 2; if (fabs(actual_reduction) <= tolerance && predicted_reduction <= tolerance && 0.5 * Ratio <= 1.0 && info == 2) info = 3; if (info != 0) break; // Tests for stringent tolerance if (nfev >= maxfev) info = 5; if (fabs(actual_reduction) <= machine_epsilon && predicted_reduction <= machine_epsilon && 0.5 * Ratio <= 1.0) info = 6; if (delta <= machine_epsilon * xnorm) info = 7; if (gnorm <= machine_epsilon) info = 8; if (info != 0) break; } // END inner loop if (info != 0) break; currentIt++; } // Final parameters and Y at final parameters Params_to_Pvec(ParamVec, Params_); fxn_(Xvals_, ParamVec, finalY_); # ifdef DBG_CURVEFIT DBGPRINT("%s\n", Message(info)); DBGPRINT("Exiting with info value = %i\n", info); for (dsize in = 0; in != n_; in++) DBGPRINT("\tParams[%lu]= %g\n", in, ParamVec[in]); # endif return info; }