/// <summary> /// 複数ピークをフィッティングする. 戻り値は、R値 /// </summary> /// <param name="pt">フィッティング対象プロファイルのデータ配列</param> /// <param name="BackgroundFitting">バックグラウンドをフィッティングするかどうか</param> /// <param name="RemoveBadSN">SN比の悪いピークについてはNaNを返すかどうか</param> /// <param name="p">ピーク関数 格納されている中心値、半値幅、フィッティングRangeを設定しておく</param> public static double FitMultiPeaksThread(PointD[] pt, bool BackgroundFitting, double RemoveBadSN, ref PeakFunction[] p) { //マルカールの方法でPseudoVoigtをとく。高速になるはず if (p.Length == 0) { return(double.PositiveInfinity); } if (pt == null || pt.Length < 3) { return(double.PositiveInfinity); } //まずここからプロファイルをとる領域や強度ななどの情報を集める List <double> PtX = new List <double>(); List <double> PtY = new List <double>(); double sum = 0; double temp = double.NegativeInfinity; double tempMin = double.PositiveInfinity; //指定された範囲内のプロファイルをPtX,PtYに格納 for (int i = 0; i < pt.Length; i++) { if (pt[i].X >= Math.Min(p[0].X - p[0].range, p[p.Length - 1].X - p[p.Length - 1].range) && pt[i].X <= Math.Max(p[0].X + p[0].range, p[p.Length - 1].X + p[p.Length - 1].range)) { PtX.Add(pt[i].X); PtY.Add(pt[i].Y / 1000); sum += pt[i].Y / 1000; if (temp < pt[i].Y / 1000) { temp = pt[i].Y / 1000; } } } //p[i].SerchPeakTop for (int i = 0; i < p.Length; i++) { if (p[i].SerchPeakTop) { double max = double.NegativeInfinity; double tempX = 0; for (int j = 0; j < PtX.Count; j++) { if (PtX[j] >= p[i].X - p[i].range && PtX[j] <= p[0].X + p[0].range) { if (max < PtY[j]) { max = PtY[j]; tempX = PtX[j]; } } } p[i].X = tempX; } } //最も小さい強度を探す for (int i = 0; i < pt.Length; i++) { if (pt[i].X >= Math.Min(p[0].X - p[0].range, p[p.Length - 1].X - p[p.Length - 1].range) && pt[i].X <= Math.Max(p[0].X + p[0].range, p[p.Length - 1].X + p[p.Length - 1].range)) { if (tempMin > pt[i].Y / 1000) { tempMin = pt[i].Y / 1000; } } } if (PtY.Count < 3 || /*temp <= 0 ||*/ (PtY[PtY.Count - 1] == 0 && PtY[PtY.Count - 2] == 0 && PtY[PtY.Count - 3] == 0)) { for (int i = 0; i < p.Length; i++) { p[i].X = double.NaN; p[i].Int = double.NaN; } return(double.PositiveInfinity); } double[] x = PtX.ToArray(); double[] y = PtY.ToArray(); int length = x.Length; //ここまででプロファイルの領域などを決定 //ここから決めなければいけないパラメータの数を設定する int ParamNum = 0; for (int i = 0; i < p.Length; i++) { ParamNum += p[i].GetParamNumber(); } if (ParamNum < 0) { for (int i = 0; i < p.Length; i++) { p[i].X = double.NaN; p[i].Int = double.NaN; } return(double.PositiveInfinity); } var diff = new double[ParamNum + 2, length]; var Alpha = new DenseMatrix(ParamNum + 2, ParamNum + 2); var Beta = new DenseMatrix(ParamNum + 2, 1); var pCurrent = new PeakFunction[p.Length]; var pNew = new PeakFunction[p.Length]; for (int i = 0; i < p.Length; i++) { pCurrent[i] = new PeakFunction(); pNew[i] = new PeakFunction(); pCurrent[i] = p[i].Copy(); } //ここまで //Intの値を大雑把に決める var peakIntensity = new double[p.Length]; for (var i = 0; i < p.Length; i++) { var d = double.PositiveInfinity; for (int j = 0; j < PtX.Count; j++) { if (d > Math.Abs(x[j] - p[i].X)) { d = Math.Abs(x[j] - p[i].X); peakIntensity[i] = y[j]; } } peakIntensity[i] -= tempMin; } //相対値として再配分する peakIntensity = Statistics.Normarize(peakIntensity); double[] ResidualCurrent = new double[length]; double[] ResidualNew = new double[length]; double ResidualSquareCurrent; double ResidualSquareNew = 0; double residual; double centerX = (x[0] + x[x.Length - 1]) / 2; double B1, B2, B1_New, B2_New; B1 = B2 = 0; double bestResidual = double.PositiveInfinity; int bestInitial = 0; int startInitial = 0; int endInitial = 1;// 2; int counter = 0; for (int Initial = startInitial; Initial < endInitial; Initial++) { double[] c = new double[3]; switch (Initial) { case 00: c = new double[] { 1, 1, 1 }; break; case 01: c = new double[] { 1, 1, 2 }; break; case 02: c = new double[] { 1, 1, 0.5 }; break; case 03: c = new double[] { 0.5, 1, 1 }; break; case 04: c = new double[] { 0.5, 1, 2 }; break; case 05: c = new double[] { 0.5, 1, 0.5 }; break; case 06: c = new double[] { 2, 1, 1 }; break; case 07: c = new double[] { 2, 1, 0.5 }; break; case 08: c = new double[] { 2, 1, 2 }; break; case 09: c = new double[] { 1, 2, 1 }; break; case 10: c = new double[] { 1, 0.5, 1 }; break; case 11: c = new double[] { 0.5, 2, 2 }; break; } //ここから初期値をきめる //Xはすでに代入済み //Int以外のの初期値を大雑把に決める for (int i = 0; i < p.Length; i++) { pCurrent[i].X = p[i].X; if (p[i].Hk > 0) { pCurrent[i].Hk = p[i].Hk * c[0]; } else { pCurrent[i].Hk = p[i].range * 0.5 * c[0]; } pCurrent[i].eta = 0.5 * c[1]; pCurrent[i].etaH = 0.5 * c[1]; pCurrent[i].etaL = 0.5 * c[1]; pCurrent[i].m = 2 * c[1]; pCurrent[i].Rl = 2 * c[1]; pCurrent[i].Rh = 2 * c[1]; pCurrent[i].A = 0; } B1 = tempMin; B2 = 0; //Int for (int i = 0; i < p.Length; i++) { pCurrent[i].Int = 1; pCurrent[i].Int = (sum - tempMin * x.Length) * (x[1] - x[0]) * peakIntensity[i] / pCurrent[i].GetIntegral() * c[2]; } //ここまで初期値決め //現在の残差を計算 ResidualSquareCurrent = 0; double[] IntCurrent = new double[x.Length]; double[] IntNew = new double[x.Length]; for (int i = 0; i < length; i++) { IntCurrent[i] = B1 + B2 * (x[i] - centerX); } for (int j = 0; j < p.Length; j++) { pCurrent[j].RenewParameter(); for (int i = 0; i < length; i++) { IntCurrent[i] += pCurrent[j].GetValue(x[i], false); } } for (int i = 0; i < length; i++) { ResidualCurrent[i] = y[i] - IntCurrent[i]; ResidualSquareCurrent += ResidualCurrent[i] * ResidualCurrent[i]; } double ramda = 100; counter = 0; do { counter++; //偏微分を作る int n = 0; int m = 0; for (int j = 0; j < p.Length; j++) { n = m; pCurrent[j].RenewParameter(); for (int i = 0; i < length; i++) { m = n; double[] d = pCurrent[j].GetDifferentialValue(x[i], false); for (int k = 0; k < d.Length; k++) { diff[m, i] = d[k]; m++; } } } for (int i = 0; i < length; i++) {//バックグラウンド変数の偏微分 diff[ParamNum, i] = 1; diff[ParamNum + 1, i] = x[i] - centerX; } //偏微分を作る ここまで //行列Alpha, Betaを作る for (int i = 0; i < ParamNum + 2; i++) { for (int j = i; j < ParamNum + 2; j++) { Alpha[i, j] = 0; for (int k = 0; k < length; k++) { Alpha[i, j] += diff[i, k] * diff[j, k]; } Alpha[j, i] = Alpha[i, j]; if (i == j) { Alpha[i, j] *= (1 + ramda); } } Beta[i, 0] = 0; for (int k = 0; k < length; k++) { Beta[i, 0] += ResidualCurrent[k] * diff[i, k]; } } //行列Alpha、Beta、ここまで var alphaInv = Alpha.TryInverse(); if (alphaInv == null) { for (int i = 0; i < p.Length; i++) { p[i].X = double.NaN; p[i].Int = double.NaN; } return(double.PositiveInfinity); } var delta = alphaInv * Beta; //新しいパラメータをセットして適宜修正 n = 0; bool flag = true; for (int j = 0; j < p.Length; j++) { pNew[j] = pCurrent[j].Copy(); if (p[j].Option == PeakFunctionForm.PseudoVoigt)//PseudoVoigt: Int, Eta, Hk, X ; { pNew[j].Int = pCurrent[j].Int + delta[n++, 0]; pNew[j].eta = pCurrent[j].eta + delta[n++, 0]; pNew[j].Hk = pCurrent[j].Hk + delta[n++, 0]; pNew[j].X = pCurrent[j].X + delta[n++, 0]; //if (pNew[j].eta < 0 || pNew[j].eta > 1) // flag = false; if (pNew[j].eta < 0) { pNew[j].eta = 0; } if (pNew[j].eta > 1) { pNew[j].eta = 1; } } else if (p[j].Option == PeakFunctionForm.Peason)//PearsonVII: Int, Hk, m, X { pNew[j].Int = pCurrent[j].Int + delta[n++, 0]; pNew[j].Hk = pCurrent[j].Hk + delta[n++, 0]; pNew[j].m = pCurrent[j].m + delta[n++, 0]; pNew[j].X = pCurrent[j].X + delta[n++, 0]; if (pNew[j].m <= 0.5 || pNew[j].m >= 10) { flag = false; } } else if (p[j].Option == PeakFunctionForm.SplitPseudoVoigt)//SplitPseudoVoigt: Int, Hk, A, etaL , etaH, X { pNew[j].Int = pCurrent[j].Int + delta[n++, 0]; pNew[j].Hk = pCurrent[j].Hk + delta[n++, 0]; pNew[j].A = pCurrent[j].A + delta[n++, 0]; pNew[j].etaL = pCurrent[j].etaL + delta[n++, 0]; pNew[j].etaH = pCurrent[j].etaH + delta[n++, 0]; pNew[j].X = pCurrent[j].X + delta[n++, 0]; if (pNew[j].etaL < 0 || pNew[j].etaL > 1) { flag = false; } if (pNew[j].etaH < 0 || pNew[j].etaH > 1) { flag = false; } if (pNew[j].A < -2 || pNew[j].A > 2) { flag = false; } } else if (p[j].Option == PeakFunctionForm.SplitPearson)//SplitPearsonVII: Int, Hk, A, Rl , Rh, X { pNew[j].Int = pCurrent[j].Int + delta[n++, 0]; pNew[j].Hk = pCurrent[j].Hk + delta[n++, 0]; pNew[j].A = pCurrent[j].A + delta[n++, 0]; pNew[j].Rl = pCurrent[j].Rl + delta[n++, 0]; pNew[j].Rh = pCurrent[j].Rh + delta[n++, 0]; pNew[j].X = pCurrent[j].X + delta[n++, 0]; if (pNew[j].Rl <= 0.5 || pNew[j].Rl >= 10) { flag = false; } if (pNew[j].Rh <= 0.5 || pNew[j].Rh >= 10) { flag = false; } if (pNew[j].A < -2 || pNew[j].A > 2) { flag = false; } } if (pNew[j].Hk < 0) { flag = false; //pNew[j].Hk = pCurrent[j].Hk; } //2本以上のフィッティングの場合、XがRangeの1/8以上動いたらもどす if (p.Length > 1) { if (pNew[j].X - p[j].X > p[j].range / 8 || pNew[j].X - p[j].X < -p[j].range / 8) { pNew[j].X = pCurrent[j].X; } } // flag = false; //pNew[j].X = p[j].X + p[j].range / 4; } B1_New = B1 + delta[ParamNum, 0]; B2_New = B2 + delta[ParamNum + 1, 0]; //if (B1_New + B2_New * (x[0] - centerX) < 0 || B1_New + B2_New * (x[x.Length - 1] - centerX) < 0) // flag = false; //B1_New = B1;//B2_New = B2; //あたらしいパラメータでの残差を計算 if (flag) { ResidualSquareNew = 0; for (int i = 0; i < length; i++) { IntNew[i] = B1_New + B2_New * (x[i] - centerX); } for (int j = 0; j < p.Length; j++) { pNew[j].RenewParameter(); for (int i = 0; i < length; i++) { IntNew[i] += pNew[j].GetValue(x[i], false); } } for (int i = 0; i < length; i++) { ResidualNew[i] = y[i] - IntNew[i]; ResidualSquareNew += ResidualNew[i] * ResidualNew[i]; } } //残差計算ここまで //新旧の値を比較 if (flag && ResidualSquareCurrent >= ResidualSquareNew) { ramda *= 0.8; for (int i = 0; i < length; i++) { ResidualCurrent[i] = ResidualNew[i]; } for (int j = 0; j < p.Length; j++) { pCurrent[j] = pNew[j].Copy(); } B1 = B1_New; B2 = B2_New; if (counter > 50 && (ResidualSquareCurrent - ResidualSquareNew) / ResidualSquareCurrent < 0.0000000001) { break; } ResidualSquareCurrent = ResidualSquareNew; } else { ramda *= 2; } } while (ramda < 100000000000 && counter < 1000); for (int i = 0; i < length; i++) { IntNew[i] = B1_New + B2_New * (x[i] - centerX); } for (int j = 0; j < p.Length; j++) { pNew[j].RenewParameter(); for (int i = 0; i < length; i++) { IntNew[i] += pNew[j].GetValue(x[i], false); } } residual = 0; for (int i = 0; i < length; i++) { residual += (IntNew[i] - y[i]) / IntNew[i] * (IntNew[i] - y[i]) / IntNew[i]; } residual /= length; if (bestResidual > residual)//最も結果のよかったカウントを格納 { bestResidual = residual; bestInitial = Initial; } if (Math.Sqrt(residual) < 0.25)//平均2乗残差が5%以下だったら終了 { break; } if (Initial == endInitial - 1 && endInitial - startInitial > 2)//5%以下のものが見つからなかったとき { //50%を超えるくらいひどかったら if (Math.Sqrt(bestResidual) > 0.90) { for (int i = 0; i < p.Length; i++) { p[i].X = double.NaN; p[i].Int = double.NaN; } return(double.PositiveInfinity); } //一番ましな初期値でやり直す startInitial = bestInitial - 1; Initial = bestInitial - 1; endInitial = bestInitial + 1; } } //行列アルファを決める //行列Alpha, Betaを作る for (int i = 0; i < ParamNum + 2; i++) { for (int j = i; j < ParamNum + 2; j++) { Alpha[i, j] = 0; for (int k = 0; k < length; k++) { Alpha[i, j] += diff[i, k] * diff[j, k]; } Alpha[j, i] = Alpha[i, j]; } } counter = 0; //最後にそれぞれもとまった数値を入れる。 for (int i = 0; i < p.Length; i++) { //p[i] = pCurrent[i].Copy(); p[i].Option = pCurrent[i].Option; p[i].intensity = pCurrent[i].intensity; p[i].eta = pCurrent[i].eta; p[i].etaH = pCurrent[i].etaH; p[i].etaL = pCurrent[i].etaL; p[i].Rl = pCurrent[i].Rl; p[i].Rh = pCurrent[i].Rh; p[i].X = pCurrent[i].X; p[i].Hk = pCurrent[i].Hk; p[i].Int = pCurrent[i].Int; p[i].A = pCurrent[i].A; p[i].B1 = pCurrent[i].B1; p[i].B2 = pCurrent[i].B2; p[i].B3 = pCurrent[i].B3; counter += p[i].GetParamNumber(); p[i].Xerr = 1 / Math.Sqrt(Alpha[counter - 1, counter - 1]); p[i].m = pCurrent[i].m; p[i].range = pCurrent[i].range; p[i].Residual = bestResidual; p[i].Int *= 1000; p[i].B1 = B1 + B2 * (pCurrent[i].X - centerX); p[i].B2 = B2; p[i].B1 *= 1000; p[i].B2 *= 1000; } //これより下は主にIPAからだけ呼ばれる部分 if (RemoveBadSN > 0) { //S/N比が悪いデータは除去する double BackGround = 0; double Signal = 0; p[0].RenewParameter(); for (int i = 0; i < length; i++) { Signal += p[0].GetValue(x[i], false); BackGround += p[0].B1 + p[0].B2 * (p[0].X - x[i]); } if (double.IsNaN(Signal / (BackGround + Signal)) || double.IsInfinity(Signal / (BackGround + Signal)) || (Signal / (BackGround + Signal)) < RemoveBadSN) { p[0].X = double.NaN; } //中心位置が外れて過ぎても失格 if (p[0].X < x[0] || x[x.Length - 1] < p[0].X) { p[0].X = double.NaN; } } /*if (counter < 1000) * return true; * else * return false;*/ return(bestResidual); }