[9129] | 1 | #region License Information
|
---|
| 2 | /* HeuristicLab
|
---|
[12012] | 3 | * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
|
---|
[9129] | 4 | *
|
---|
| 5 | * This file is part of HeuristicLab.
|
---|
| 6 | *
|
---|
| 7 | * HeuristicLab is free software: you can redistribute it and/or modify
|
---|
| 8 | * it under the terms of the GNU General Public License as published by
|
---|
| 9 | * the Free Software Foundation, either version 3 of the License, or
|
---|
| 10 | * (at your option) any later version.
|
---|
| 11 | *
|
---|
| 12 | * HeuristicLab is distributed in the hope that it will be useful,
|
---|
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
| 15 | * GNU General Public License for more details.
|
---|
| 16 | *
|
---|
| 17 | * You should have received a copy of the GNU General Public License
|
---|
| 18 | * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
|
---|
| 19 | */
|
---|
| 20 | #endregion
|
---|
| 21 |
|
---|
| 22 | using HeuristicLab.Common;
|
---|
| 23 | using HeuristicLab.Core;
|
---|
| 24 | using HeuristicLab.Data;
|
---|
| 25 | using HeuristicLab.Encodings.RealVectorEncoding;
|
---|
| 26 | using HeuristicLab.Operators;
|
---|
| 27 | using HeuristicLab.Optimization;
|
---|
| 28 | using HeuristicLab.Parameters;
|
---|
| 29 | using HeuristicLab.Persistence.Default.CompositeSerializers.Storable;
|
---|
| 30 | using System;
|
---|
| 31 | using System.Linq;
|
---|
| 32 |
|
---|
| 33 | namespace HeuristicLab.Algorithms.CMAEvolutionStrategy {
|
---|
| 34 | [Item("CMAUpdater", "Updates the covariance matrix and strategy parameters of CMA-ES.")]
|
---|
| 35 | [StorableClass]
|
---|
[11970] | 36 | public class CMAUpdater : SingleSuccessorOperator, ICMAUpdater, IIterationBasedOperator, ISingleObjectiveOperator {
|
---|
[9129] | 37 |
|
---|
| 38 | public Type CMAType {
|
---|
| 39 | get { return typeof(CMAParameters); }
|
---|
| 40 | }
|
---|
| 41 |
|
---|
| 42 | #region Parameter Properties
|
---|
| 43 | public ILookupParameter<CMAParameters> StrategyParametersParameter {
|
---|
| 44 | get { return (ILookupParameter<CMAParameters>)Parameters["StrategyParameters"]; }
|
---|
| 45 | }
|
---|
| 46 |
|
---|
| 47 | public ILookupParameter<RealVector> MeanParameter {
|
---|
| 48 | get { return (ILookupParameter<RealVector>)Parameters["Mean"]; }
|
---|
| 49 | }
|
---|
| 50 |
|
---|
| 51 | public ILookupParameter<RealVector> OldMeanParameter {
|
---|
| 52 | get { return (ILookupParameter<RealVector>)Parameters["OldMean"]; }
|
---|
| 53 | }
|
---|
| 54 |
|
---|
| 55 | public IScopeTreeLookupParameter<RealVector> OffspringParameter {
|
---|
| 56 | get { return (IScopeTreeLookupParameter<RealVector>)Parameters["Offspring"]; }
|
---|
| 57 | }
|
---|
| 58 |
|
---|
| 59 | public IScopeTreeLookupParameter<DoubleValue> QualityParameter {
|
---|
| 60 | get { return (IScopeTreeLookupParameter<DoubleValue>)Parameters["Quality"]; }
|
---|
| 61 | }
|
---|
| 62 |
|
---|
| 63 | public ILookupParameter<IntValue> IterationsParameter {
|
---|
| 64 | get { return (ILookupParameter<IntValue>)Parameters["Iterations"]; }
|
---|
| 65 | }
|
---|
| 66 |
|
---|
| 67 | public IValueLookupParameter<IntValue> MaximumIterationsParameter {
|
---|
| 68 | get { return (IValueLookupParameter<IntValue>)Parameters["MaximumIterations"]; }
|
---|
| 69 | }
|
---|
| 70 |
|
---|
| 71 | public IValueLookupParameter<IntValue> MaximumEvaluatedSolutionsParameter {
|
---|
| 72 | get { return (IValueLookupParameter<IntValue>)Parameters["MaximumEvaluatedSolutions"]; }
|
---|
| 73 | }
|
---|
| 74 |
|
---|
| 75 | public ILookupParameter<BoolValue> DegenerateStateParameter {
|
---|
| 76 | get { return (ILookupParameter<BoolValue>)Parameters["DegenerateState"]; }
|
---|
| 77 | }
|
---|
| 78 | #endregion
|
---|
| 79 |
|
---|
| 80 | [StorableConstructor]
|
---|
| 81 | protected CMAUpdater(bool deserializing) : base(deserializing) { }
|
---|
| 82 | protected CMAUpdater(CMAUpdater original, Cloner cloner) : base(original, cloner) { }
|
---|
| 83 | public CMAUpdater()
|
---|
| 84 | : base() {
|
---|
| 85 | Parameters.Add(new LookupParameter<CMAParameters>("StrategyParameters", "The strategy parameters of CMA-ES."));
|
---|
| 86 | Parameters.Add(new LookupParameter<RealVector>("Mean", "The new mean."));
|
---|
| 87 | Parameters.Add(new LookupParameter<RealVector>("OldMean", "The old mean."));
|
---|
| 88 | Parameters.Add(new ScopeTreeLookupParameter<RealVector>("Offspring", "The created offspring solutions."));
|
---|
| 89 | Parameters.Add(new ScopeTreeLookupParameter<DoubleValue>("Quality", "The quality of the offspring."));
|
---|
| 90 | Parameters.Add(new LookupParameter<IntValue>("Iterations", "The number of iterations passed."));
|
---|
| 91 | Parameters.Add(new ValueLookupParameter<IntValue>("MaximumIterations", "The maximum number of iterations."));
|
---|
| 92 | Parameters.Add(new ValueLookupParameter<IntValue>("MaximumEvaluatedSolutions", "The maximum number of evaluated solutions."));
|
---|
| 93 | Parameters.Add(new LookupParameter<BoolValue>("DegenerateState", "Whether the algorithm state has degenerated and should be terminated."));
|
---|
| 94 | MeanParameter.ActualName = "XMean";
|
---|
| 95 | OldMeanParameter.ActualName = "XOld";
|
---|
| 96 | OffspringParameter.ActualName = "RealVector";
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | public override IDeepCloneable Clone(Cloner cloner) {
|
---|
| 100 | return new CMAUpdater(this, cloner);
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | public override IOperation Apply() {
|
---|
| 104 | var iterations = IterationsParameter.ActualValue.Value;
|
---|
| 105 |
|
---|
| 106 | var xold = OldMeanParameter.ActualValue;
|
---|
| 107 | var xmean = MeanParameter.ActualValue;
|
---|
| 108 | var offspring = OffspringParameter.ActualValue;
|
---|
| 109 | var quality = QualityParameter.ActualValue;
|
---|
| 110 | var lambda = offspring.Length;
|
---|
| 111 |
|
---|
| 112 | var N = xmean.Length;
|
---|
| 113 | var sp = StrategyParametersParameter.ActualValue;
|
---|
| 114 |
|
---|
| 115 | #region Initialize default values for strategy parameter adjustment
|
---|
[9297] | 116 | if (sp.ChiN == 0) sp.ChiN = Math.Sqrt(N) * (1.0 - 1.0 / (4.0 * N) + 1.0 / (21.0 * N * N));
|
---|
| 117 | if (sp.MuEff == 0) sp.MuEff = sp.Weights.Sum() * sp.Weights.Sum() / sp.Weights.Sum(x => x * x);
|
---|
| 118 | if (sp.CS == 0) sp.CS = (sp.MuEff + 2) / (N + sp.MuEff + 3);
|
---|
| 119 | if (sp.Damps == 0) {
|
---|
[9129] | 120 | var maxIterations = MaximumIterationsParameter.ActualValue.Value;
|
---|
| 121 | var maxEvals = MaximumEvaluatedSolutionsParameter.ActualValue.Value;
|
---|
[9297] | 122 | sp.Damps = 2 * Math.Max(0, Math.Sqrt((sp.MuEff - 1) / (N + 1)) - 1)
|
---|
| 123 | * Math.Max(0.3, 1 - N / (1e-6 + Math.Min(maxIterations, maxEvals / lambda))) + sp.CS + 1;
|
---|
[9129] | 124 | }
|
---|
[9297] | 125 | if (sp.CC == 0) sp.CC = 4.0 / (N + 4);
|
---|
| 126 | if (sp.MuCov == 0) sp.MuCov = sp.MuEff;
|
---|
[9298] | 127 | if (sp.CCov == 0) sp.CCov = 2.0 / ((N + 1.41) * (N + 1.41) * sp.MuCov)
|
---|
| 128 | + (1 - (1.0 / sp.MuCov)) * Math.Min(1, (2 * sp.MuEff - 1) / (sp.MuEff + (N + 2) * (N + 2)));
|
---|
[9297] | 129 | if (sp.CCovSep == 0) sp.CCovSep = Math.Min(1, sp.CCov * (N + 1.5) / 3);
|
---|
[9129] | 130 | #endregion
|
---|
| 131 |
|
---|
| 132 | sp.QualityHistory.Enqueue(quality[0].Value);
|
---|
| 133 | while (sp.QualityHistory.Count > sp.QualityHistorySize && sp.QualityHistorySize >= 0)
|
---|
| 134 | sp.QualityHistory.Dequeue();
|
---|
| 135 |
|
---|
| 136 | for (int i = 0; i < N; i++) {
|
---|
[9297] | 137 | sp.BDz[i] = Math.Sqrt(sp.MuEff) * (xmean[i] - xold[i]) / sp.Sigma;
|
---|
[9129] | 138 | }
|
---|
| 139 |
|
---|
[9297] | 140 | if (sp.InitialIterations >= iterations) {
|
---|
[9129] | 141 | for (int i = 0; i < N; i++) {
|
---|
[9297] | 142 | sp.PS[i] = (1 - sp.CS) * sp.PS[i]
|
---|
| 143 | + Math.Sqrt(sp.CS * (2 - sp.CS)) * sp.BDz[i] / sp.D[i];
|
---|
[9129] | 144 | }
|
---|
| 145 | } else {
|
---|
| 146 | var artmp = new double[N];
|
---|
| 147 | for (int i = 0; i < N; i++) {
|
---|
| 148 | var sum = 0.0;
|
---|
| 149 | for (int j = 0; j < N; j++) {
|
---|
| 150 | sum += sp.B[j, i] * sp.BDz[j];
|
---|
| 151 | }
|
---|
| 152 | artmp[i] = sum / sp.D[i];
|
---|
| 153 | }
|
---|
| 154 | for (int i = 0; i < N; i++) {
|
---|
| 155 | var sum = 0.0;
|
---|
| 156 | for (int j = 0; j < N; j++) {
|
---|
| 157 | sum += sp.B[i, j] * artmp[j];
|
---|
| 158 | }
|
---|
[9297] | 159 | sp.PS[i] = (1 - sp.CS) * sp.PS[i] + Math.Sqrt(sp.CS * (2 - sp.CS)) * sum;
|
---|
[9129] | 160 | }
|
---|
| 161 | }
|
---|
| 162 | var normPS = Math.Sqrt(sp.PS.Select(x => x * x).Sum());
|
---|
[9297] | 163 | var hsig = normPS / Math.Sqrt(1 - Math.Pow(1 - sp.CS, 2 * iterations)) / sp.ChiN < 1.4 + 2.0 / (N + 1) ? 1.0 : 0.0;
|
---|
[9129] | 164 | for (int i = 0; i < sp.PC.Length; i++) {
|
---|
[9297] | 165 | sp.PC[i] = (1 - sp.CC) * sp.PC[i]
|
---|
| 166 | + hsig * Math.Sqrt(sp.CC * (2 - sp.CC)) * sp.BDz[i];
|
---|
[9129] | 167 | }
|
---|
| 168 |
|
---|
[9297] | 169 | if (sp.CCov > 0) {
|
---|
| 170 | if (sp.InitialIterations >= iterations) {
|
---|
[9129] | 171 | for (int i = 0; i < N; i++) {
|
---|
[9297] | 172 | sp.C[i, i] = (1 - sp.CCovSep) * sp.C[i, i]
|
---|
| 173 | + sp.CCov * (1 / sp.MuCov)
|
---|
| 174 | * (sp.PC[i] * sp.PC[i] + (1 - hsig) * sp.CC * (2 - sp.CC) * sp.C[i, i]);
|
---|
| 175 | for (int k = 0; k < sp.Mu; k++) {
|
---|
| 176 | sp.C[i, i] += sp.CCov * (1 - 1 / sp.MuCov) * sp.Weights[k] * (offspring[k][i] - xold[i]) *
|
---|
| 177 | (offspring[k][i] - xold[i]) / (sp.Sigma * sp.Sigma);
|
---|
[9129] | 178 | }
|
---|
| 179 | }
|
---|
| 180 | } else {
|
---|
| 181 | for (int i = 0; i < N; i++) {
|
---|
| 182 | for (int j = 0; j < N; j++) {
|
---|
[9297] | 183 | sp.C[i, j] = (1 - sp.CCov) * sp.C[i, j]
|
---|
| 184 | + sp.CCov * (1 / sp.MuCov)
|
---|
| 185 | * (sp.PC[i] * sp.PC[j] + (1 - hsig) * sp.CC * (2 - sp.CC) * sp.C[i, j]);
|
---|
| 186 | for (int k = 0; k < sp.Mu; k++) {
|
---|
| 187 | sp.C[i, j] += sp.CCov * (1 - 1 / sp.MuCov) * sp.Weights[k] * (offspring[k][i] - xold[i]) *
|
---|
| 188 | (offspring[k][j] - xold[j]) / (sp.Sigma * sp.Sigma);
|
---|
[9129] | 189 | }
|
---|
| 190 | }
|
---|
| 191 | }
|
---|
| 192 | }
|
---|
| 193 | }
|
---|
[9297] | 194 | sp.Sigma *= Math.Exp((sp.CS / sp.Damps) * (normPS / sp.ChiN - 1));
|
---|
[9129] | 195 |
|
---|
| 196 | double minSqrtdiagC = int.MaxValue, maxSqrtdiagC = int.MinValue;
|
---|
| 197 | for (int i = 0; i < N; i++) {
|
---|
| 198 | if (Math.Sqrt(sp.C[i, i]) < minSqrtdiagC) minSqrtdiagC = Math.Sqrt(sp.C[i, i]);
|
---|
| 199 | if (Math.Sqrt(sp.C[i, i]) > maxSqrtdiagC) maxSqrtdiagC = Math.Sqrt(sp.C[i, i]);
|
---|
| 200 | }
|
---|
| 201 |
|
---|
| 202 | // ensure maximal and minimal standard deviations
|
---|
[9297] | 203 | if (sp.SigmaBounds != null && sp.SigmaBounds.GetLength(0) > 0) {
|
---|
[9129] | 204 | for (int i = 0; i < N; i++) {
|
---|
[9297] | 205 | var d = sp.SigmaBounds[Math.Min(i, sp.SigmaBounds.GetLength(0) - 1), 0];
|
---|
| 206 | if (d > sp.Sigma * minSqrtdiagC) sp.Sigma = d / minSqrtdiagC;
|
---|
[9129] | 207 | }
|
---|
| 208 | for (int i = 0; i < N; i++) {
|
---|
[9297] | 209 | var d = sp.SigmaBounds[Math.Min(i, sp.SigmaBounds.GetLength(0) - 1), 1];
|
---|
| 210 | if (d > sp.Sigma * maxSqrtdiagC) sp.Sigma = d / maxSqrtdiagC;
|
---|
[9129] | 211 | }
|
---|
| 212 | }
|
---|
| 213 | // end ensure ...
|
---|
| 214 |
|
---|
| 215 | // testAndCorrectNumerics
|
---|
| 216 | double fac = 1;
|
---|
| 217 | if (sp.D.Max() < 1e-6)
|
---|
| 218 | fac = 1.0 / sp.D.Max();
|
---|
| 219 | else if (sp.D.Min() > 1e4)
|
---|
| 220 | fac = 1.0 / sp.D.Min();
|
---|
| 221 |
|
---|
| 222 | if (fac != 1.0) {
|
---|
[9297] | 223 | sp.Sigma /= fac;
|
---|
[9129] | 224 | for (int i = 0; i < N; i++) {
|
---|
| 225 | sp.PC[i] *= fac;
|
---|
| 226 | sp.D[i] *= fac;
|
---|
| 227 | for (int j = 0; j < N; j++)
|
---|
| 228 | sp.C[i, j] *= fac * fac;
|
---|
| 229 | }
|
---|
| 230 | }
|
---|
| 231 | // end testAndCorrectNumerics
|
---|
| 232 |
|
---|
[9148] | 233 |
|
---|
[9297] | 234 | if (sp.InitialIterations >= iterations) {
|
---|
[9129] | 235 | for (int i = 0; i < N; i++)
|
---|
| 236 | sp.D[i] = Math.Sqrt(sp.C[i, i]);
|
---|
[9148] | 237 | DegenerateStateParameter.ActualValue = new BoolValue(false);
|
---|
[9129] | 238 | } else {
|
---|
[9148] | 239 |
|
---|
[9291] | 240 | // set B <- C
|
---|
[9297] | 241 | for (int i = 0; i < N; i++) {
|
---|
| 242 | for (int j = 0; j < N; j++) {
|
---|
| 243 | sp.B[i, j] = sp.C[i, j];
|
---|
| 244 | }
|
---|
| 245 | }
|
---|
| 246 | var success = Eigendecomposition(N, sp.B, sp.D);
|
---|
[9291] | 247 |
|
---|
[9129] | 248 | DegenerateStateParameter.ActualValue = new BoolValue(!success);
|
---|
| 249 |
|
---|
[9297] | 250 | // assign D to eigenvalue square roots
|
---|
| 251 | for (int i = 0; i < N; i++) {
|
---|
| 252 | if (sp.D[i] < 0) { // numerical problem?
|
---|
[9148] | 253 | DegenerateStateParameter.ActualValue.Value = true;
|
---|
| 254 | sp.D[i] = 0;
|
---|
[9297] | 255 | } else sp.D[i] = Math.Sqrt(sp.D[i]);
|
---|
| 256 | }
|
---|
[9129] | 257 |
|
---|
[9297] | 258 | if (sp.D.Min() == 0.0) sp.AxisRatio = double.PositiveInfinity;
|
---|
| 259 | else sp.AxisRatio = sp.D.Max() / sp.D.Min();
|
---|
[9129] | 260 | }
|
---|
| 261 | return base.Apply();
|
---|
| 262 | }
|
---|
[9291] | 263 |
|
---|
[9297] | 264 | private bool Eigendecomposition(int N, double[,] B, double[] diagD) {
|
---|
[9291] | 265 | bool result = true;
|
---|
| 266 | // eigendecomposition
|
---|
| 267 | var offdiag = new double[N];
|
---|
[9297] | 268 | try {
|
---|
| 269 | tred2(N, B, diagD, offdiag);
|
---|
| 270 | tql2(N, diagD, offdiag, B);
|
---|
| 271 | } catch { result = false; }
|
---|
[9291] | 272 |
|
---|
| 273 | return result;
|
---|
| 274 | } // eigendecomposition
|
---|
| 275 |
|
---|
| 276 |
|
---|
| 277 | // Symmetric Householder reduction to tridiagonal form, taken from JAMA package.
|
---|
[9297] | 278 | private void tred2(int n, double[,] V, double[] d, double[] e) {
|
---|
[9291] | 279 |
|
---|
| 280 | // This is derived from the Algol procedures tred2 by
|
---|
| 281 | // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
|
---|
| 282 | // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
|
---|
| 283 | // Fortran subroutine in EISPACK.
|
---|
| 284 |
|
---|
| 285 | for (int j = 0; j < n; j++) {
|
---|
| 286 | d[j] = V[n - 1, j];
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | // Householder reduction to tridiagonal form.
|
---|
| 290 |
|
---|
| 291 | for (int i = n - 1; i > 0; i--) {
|
---|
| 292 |
|
---|
| 293 | // Scale to avoid under/overflow.
|
---|
| 294 |
|
---|
| 295 | double scale = 0.0;
|
---|
| 296 | double h = 0.0;
|
---|
| 297 | for (int k = 0; k < i; k++) {
|
---|
| 298 | scale = scale + Math.Abs(d[k]);
|
---|
| 299 | }
|
---|
| 300 | if (scale == 0.0) {
|
---|
| 301 | e[i] = d[i - 1];
|
---|
| 302 | for (int j = 0; j < i; j++) {
|
---|
| 303 | d[j] = V[i - 1, j];
|
---|
| 304 | V[i, j] = 0.0;
|
---|
| 305 | V[j, i] = 0.0;
|
---|
| 306 | }
|
---|
| 307 | } else {
|
---|
| 308 |
|
---|
| 309 | // Generate Householder vector.
|
---|
| 310 |
|
---|
| 311 | for (int k = 0; k < i; k++) {
|
---|
| 312 | d[k] /= scale;
|
---|
| 313 | h += d[k] * d[k];
|
---|
| 314 | }
|
---|
| 315 | double f = d[i - 1];
|
---|
| 316 | double g = Math.Sqrt(h);
|
---|
| 317 | if (f > 0) {
|
---|
| 318 | g = -g;
|
---|
| 319 | }
|
---|
| 320 | e[i] = scale * g;
|
---|
| 321 | h = h - f * g;
|
---|
| 322 | d[i - 1] = f - g;
|
---|
| 323 | for (int j = 0; j < i; j++) {
|
---|
| 324 | e[j] = 0.0;
|
---|
| 325 | }
|
---|
| 326 |
|
---|
| 327 | // Apply similarity transformation to remaining columns.
|
---|
| 328 |
|
---|
| 329 | for (int j = 0; j < i; j++) {
|
---|
| 330 | f = d[j];
|
---|
| 331 | V[j, i] = f;
|
---|
| 332 | g = e[j] + V[j, j] * f;
|
---|
| 333 | for (int k = j + 1; k <= i - 1; k++) {
|
---|
| 334 | g += V[k, j] * d[k];
|
---|
| 335 | e[k] += V[k, j] * f;
|
---|
| 336 | }
|
---|
| 337 | e[j] = g;
|
---|
| 338 | }
|
---|
| 339 | f = 0.0;
|
---|
| 340 | for (int j = 0; j < i; j++) {
|
---|
| 341 | e[j] /= h;
|
---|
| 342 | f += e[j] * d[j];
|
---|
| 343 | }
|
---|
| 344 | double hh = f / (h + h);
|
---|
| 345 | for (int j = 0; j < i; j++) {
|
---|
| 346 | e[j] -= hh * d[j];
|
---|
| 347 | }
|
---|
| 348 | for (int j = 0; j < i; j++) {
|
---|
| 349 | f = d[j];
|
---|
| 350 | g = e[j];
|
---|
| 351 | for (int k = j; k <= i - 1; k++) {
|
---|
| 352 | V[k, j] -= (f * e[k] + g * d[k]);
|
---|
| 353 | }
|
---|
| 354 | d[j] = V[i - 1, j];
|
---|
| 355 | V[i, j] = 0.0;
|
---|
| 356 | }
|
---|
| 357 | }
|
---|
| 358 | d[i] = h;
|
---|
| 359 | }
|
---|
| 360 |
|
---|
| 361 | // Accumulate transformations.
|
---|
| 362 |
|
---|
| 363 | for (int i = 0; i < n - 1; i++) {
|
---|
| 364 | V[n - 1, i] = V[i, i];
|
---|
| 365 | V[i, i] = 1.0;
|
---|
| 366 | double h = d[i + 1];
|
---|
| 367 | if (h != 0.0) {
|
---|
| 368 | for (int k = 0; k <= i; k++) {
|
---|
| 369 | d[k] = V[k, i + 1] / h;
|
---|
| 370 | }
|
---|
| 371 | for (int j = 0; j <= i; j++) {
|
---|
| 372 | double g = 0.0;
|
---|
| 373 | for (int k = 0; k <= i; k++) {
|
---|
| 374 | g += V[k, i + 1] * V[k, j];
|
---|
| 375 | }
|
---|
| 376 | for (int k = 0; k <= i; k++) {
|
---|
| 377 | V[k, j] -= g * d[k];
|
---|
| 378 | }
|
---|
| 379 | }
|
---|
| 380 | }
|
---|
| 381 | for (int k = 0; k <= i; k++) {
|
---|
| 382 | V[k, i + 1] = 0.0;
|
---|
| 383 | }
|
---|
| 384 | }
|
---|
| 385 | for (int j = 0; j < n; j++) {
|
---|
| 386 | d[j] = V[n - 1, j];
|
---|
| 387 | V[n - 1, j] = 0.0;
|
---|
| 388 | }
|
---|
| 389 | V[n - 1, n - 1] = 1.0;
|
---|
| 390 | e[0] = 0.0;
|
---|
| 391 | }
|
---|
| 392 |
|
---|
| 393 | // Symmetric tridiagonal QL algorithm, taken from JAMA package.
|
---|
[9297] | 394 | private void tql2(int n, double[] d, double[] e, double[,] V) {
|
---|
[9291] | 395 |
|
---|
| 396 | // This is derived from the Algol procedures tql2, by
|
---|
| 397 | // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
|
---|
| 398 | // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
|
---|
| 399 | // Fortran subroutine in EISPACK.
|
---|
| 400 |
|
---|
| 401 | for (int i = 1; i < n; i++) {
|
---|
| 402 | e[i - 1] = e[i];
|
---|
| 403 | }
|
---|
| 404 | e[n - 1] = 0.0;
|
---|
| 405 |
|
---|
| 406 | double f = 0.0;
|
---|
| 407 | double tst1 = 0.0;
|
---|
| 408 | double eps = Math.Pow(2.0, -52.0);
|
---|
| 409 | for (int l = 0; l < n; l++) {
|
---|
| 410 |
|
---|
| 411 | // Find small subdiagonal element
|
---|
| 412 |
|
---|
| 413 | tst1 = Math.Max(tst1, Math.Abs(d[l]) + Math.Abs(e[l]));
|
---|
| 414 | int m = l;
|
---|
| 415 | while (m < n) {
|
---|
| 416 | if (Math.Abs(e[m]) <= eps * tst1) {
|
---|
| 417 | break;
|
---|
| 418 | }
|
---|
| 419 | m++;
|
---|
| 420 | }
|
---|
| 421 |
|
---|
| 422 | // If m == l, d[l] is an eigenvalue,
|
---|
| 423 | // otherwise, iterate.
|
---|
| 424 |
|
---|
| 425 | if (m > l) {
|
---|
| 426 | int iter = 0;
|
---|
| 427 | do {
|
---|
| 428 | iter = iter + 1; // (Could check iteration count here.)
|
---|
| 429 |
|
---|
| 430 | // Compute implicit shift
|
---|
| 431 |
|
---|
| 432 | double g = d[l];
|
---|
| 433 | double p = (d[l + 1] - g) / (2.0 * e[l]);
|
---|
| 434 | double r = hypot(p, 1.0);
|
---|
| 435 | if (p < 0) {
|
---|
| 436 | r = -r;
|
---|
| 437 | }
|
---|
| 438 | d[l] = e[l] / (p + r);
|
---|
| 439 | d[l + 1] = e[l] * (p + r);
|
---|
| 440 | double dl1 = d[l + 1];
|
---|
| 441 | double h = g - d[l];
|
---|
| 442 | for (int i = l + 2; i < n; i++) {
|
---|
| 443 | d[i] -= h;
|
---|
| 444 | }
|
---|
| 445 | f = f + h;
|
---|
| 446 |
|
---|
| 447 | // Implicit QL transformation.
|
---|
| 448 |
|
---|
| 449 | p = d[m];
|
---|
| 450 | double c = 1.0;
|
---|
| 451 | double c2 = c;
|
---|
| 452 | double c3 = c;
|
---|
| 453 | double el1 = e[l + 1];
|
---|
| 454 | double s = 0.0;
|
---|
| 455 | double s2 = 0.0;
|
---|
| 456 | for (int i = m - 1; i >= l; i--) {
|
---|
| 457 | c3 = c2;
|
---|
| 458 | c2 = c;
|
---|
| 459 | s2 = s;
|
---|
| 460 | g = c * e[i];
|
---|
| 461 | h = c * p;
|
---|
| 462 | r = hypot(p, e[i]);
|
---|
| 463 | e[i + 1] = s * r;
|
---|
| 464 | s = e[i] / r;
|
---|
| 465 | c = p / r;
|
---|
| 466 | p = c * d[i] - s * g;
|
---|
| 467 | d[i + 1] = h + s * (c * g + s * d[i]);
|
---|
| 468 |
|
---|
| 469 | // Accumulate transformation.
|
---|
| 470 |
|
---|
| 471 | for (int k = 0; k < n; k++) {
|
---|
| 472 | h = V[k, i + 1];
|
---|
| 473 | V[k, i + 1] = s * V[k, i] + c * h;
|
---|
| 474 | V[k, i] = c * V[k, i] - s * h;
|
---|
| 475 | }
|
---|
| 476 | }
|
---|
| 477 | p = -s * s2 * c3 * el1 * e[l] / dl1;
|
---|
| 478 | e[l] = s * p;
|
---|
| 479 | d[l] = c * p;
|
---|
| 480 |
|
---|
| 481 | // Check for convergence.
|
---|
| 482 |
|
---|
| 483 | } while (Math.Abs(e[l]) > eps * tst1);
|
---|
| 484 | }
|
---|
| 485 | d[l] = d[l] + f;
|
---|
| 486 | e[l] = 0.0;
|
---|
| 487 | }
|
---|
| 488 |
|
---|
| 489 | // Sort eigenvalues and corresponding vectors.
|
---|
| 490 |
|
---|
| 491 | for (int i = 0; i < n - 1; i++) {
|
---|
| 492 | int k = i;
|
---|
| 493 | double p = d[i];
|
---|
| 494 | for (int j = i + 1; j < n; j++) {
|
---|
| 495 | if (d[j] < p) { // NH find smallest k>i
|
---|
| 496 | k = j;
|
---|
| 497 | p = d[j];
|
---|
| 498 | }
|
---|
| 499 | }
|
---|
| 500 | if (k != i) {
|
---|
| 501 | d[k] = d[i]; // swap k and i
|
---|
| 502 | d[i] = p;
|
---|
| 503 | for (int j = 0; j < n; j++) {
|
---|
| 504 | p = V[j, i];
|
---|
| 505 | V[j, i] = V[j, k];
|
---|
| 506 | V[j, k] = p;
|
---|
| 507 | }
|
---|
| 508 | }
|
---|
| 509 | }
|
---|
| 510 | }
|
---|
| 511 |
|
---|
| 512 | /** sqrt(a^2 + b^2) without under/overflow. **/
|
---|
| 513 | private double hypot(double a, double b) {
|
---|
| 514 | double r = 0;
|
---|
| 515 | if (Math.Abs(a) > Math.Abs(b)) {
|
---|
| 516 | r = b / a;
|
---|
| 517 | r = Math.Abs(a) * Math.Sqrt(1 + r * r);
|
---|
| 518 | } else if (b != 0) {
|
---|
| 519 | r = a / b;
|
---|
| 520 | r = Math.Abs(b) * Math.Sqrt(1 + r * r);
|
---|
| 521 | }
|
---|
| 522 | return r;
|
---|
| 523 | }
|
---|
[9129] | 524 | }
|
---|
| 525 | } |
---|