Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Analysis.AlgorithmBehavior/HeuristicLab.Analysis.AlgorithmBehavior.Analyzers/3.3/DistanceMatrixToPoints.cs @ 10109

Last change on this file since 10109 was 10109, checked in by ascheibe, 11 years ago

#1886 fixed other MDS and added more unit tests

File size: 6.4 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2013 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
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
22using System;
23using System.Linq;
24using HeuristicLab.Common;
25
26namespace HeuristicLab.Analysis.AlgorithmBehavior.Analyzers {
27  public static class DistanceMatrixToPoints {
28    /*
29     *  Calculates a matrix of n-dimensional points from the distance matrix dm as described in
30     *  http://math.stackexchange.com/questions/156161/finding-the-coordinates-of-points-from-distance-matrix/423898#423898
31     *  and
32     *  http://stackoverflow.com/questions/10963054/finding-the-coordinates-of-points-from-distance-matrix/17177833#17177833
33     *   
34     */
35    public static double[][] ConvertDistanceMatrixToPoints(double[][] dm, int k = 2) {
36      double[][] points = new double[dm.Length][];
37      double[,] m = new double[dm.Length, dm.Length];
38      double[] q = new double[dm.Length]; //eigenvalues
39      double[,] v = new double[dm.Length, dm.Length]; //eigenvectors
40
41      for (int i = 0; i < dm.Length; i++) {
42        for (int j = 0; j < dm.Length; j++) {
43          m[i, j] = 0.5 * (Math.Pow(dm[0][j], 2) + Math.Pow(dm[i][0], 2) - Math.Pow(dm[i][j], 2));
44        }
45      }
46
47      bool res = alglib.smatrixevd(m, dm.Length, 1, true, out q, out v);
48      if (!res) throw new Exception("Eigenvalue computation did not converge!");
49
50      //TODO: this should also work without allocating memory for ev and evec
51      double[] ev = new double[k];
52      double[][] evec = new double[dm.Length][];
53      AllocArray(evec, k);
54      Array.Copy(q, q.Length - k, ev, 0, k);
55      for (int i = 0; i < k; i++) {
56        for (int j = 0; j < dm.Length; j++) {
57          evec[j][i] = v[j, i + (q.Length - k)];
58        }
59      }
60
61      double k1 = SumIfLZero(ev);
62      if (k1 < k) {
63        throw new Exception("Zero-eigenvalues detected. This leads to a degenerate point set. Use constants. ");
64        //TODO: handling of this case; implement adding of constants
65      }
66
67      AllocArray(points, k);
68      for (int i = 0; i < k; i++) {
69        for (int j = 0; j < dm.Length; j++) {
70          points[j][i] = Math.Sqrt(ev[i]) * evec[j][i];
71        }
72      }
73      return points;
74    }
75
76    //based on R's cmdscale
77    public static double[][] MetricMDS(double[][] dm, int k = 2, bool add = false) {
78      double[][] points = new double[dm.Length][];
79      double[,] b = new double[dm.Length, dm.Length];
80      double[] q; //eigenvalues
81      double[,] v; //eigenvectors
82
83      double[][] x = SquareMatrix(dm);
84      CenterMatrix(x);
85      ChangeSignAndHalve(x);
86
87      //TODO: optimize memory consumption
88      for (int i = 0; i < dm.Length; i++) {
89        for (int j = 0; j < dm.Length; j++) {
90          b[i, j] = x[i][j];
91        }
92      }
93
94      bool res = alglib.smatrixevd(b, dm.Length, 1, true, out q, out v);
95      if (!res) throw new Exception("Eigenvalue computation did not converge!");
96
97      //TODO: this should also work without allocating memory for ev and evec
98      double[] ev = new double[k];
99      double[][] evec = new double[dm.Length][];
100      AllocArray(evec, k);
101      Array.Copy(q, q.Length - k, ev, 0, k);
102      for (int i = 0; i < k; i++) {
103        for (int j = 0; j < dm.Length; j++) {
104          evec[j][i] = v[j, i + (q.Length - k)];
105        }
106      }
107
108      double k1 = SumIfLZero(ev);
109      if (k1 < k) {
110        throw new Exception("Zero-eigenvalues detected. This leads to a degenerate point set. Use constants. ");
111        //TODO: handling of this case; implement adding of constants
112      }
113
114      AllocArray(points, k);
115      for (int i = 0; i < k; i++) {
116        for (int j = 0; j < dm.Length; j++) {
117          points[j][i] = Math.Sqrt(ev[i]) * evec[j][i];
118        }
119      }
120      return points;
121    }
122
123    //TODO: refactor the following methods into something sane
124    private static double[][] SquareMatrix(double[][] a) {
125      int n = a.Length;
126      double[][] newA = new double[a.Length][];
127
128      for (int i = 0; i < n; i++) {
129        newA[i] = new double[a.Length];
130        for (int j = 0; j < n; j++) {
131          newA[i][j] = Math.Pow(a[i][j], 2.0);
132        }
133      }
134      return newA;
135    }
136
137    //based on R's DoubleCentre
138    private static void CenterMatrix(double[][] a) {
139      int n = a.Length;
140
141      //reduce lines by line avg
142      for (int i = 0; i < n; i++) {
143        double sum = 0;
144        for (int j = 0; j < n; j++) sum += a[i][j];
145        sum /= n;
146        for (int j = 0; j < n; j++) a[i][j] -= sum;
147      }
148
149      //reduce cols by col avg
150      for (int j = 0; j < n; j++) {
151        double sum = 0;
152        for (int i = 0; i < n; i++) sum += a[i][j];
153        sum /= n;
154        for (int i = 0; i < n; i++) a[i][j] -= sum;
155      }
156    }
157
158    private static void ChangeSignAndHalve(double[][] a) {
159      int n = a.Length;
160
161      for (int i = 0; i < n; i++) {
162        for (int j = 0; j < n; j++) {
163          a[i][j] = (-1.0 * a[i][j]) / 2;
164        }
165      }
166    }
167
168    private static double SumIfLZero(double[] a) {
169      return a.Where(x => x > 0.0 && !x.IsAlmost(0.0)).Sum();
170    }
171
172    private static void AllocArray(double[][] arr, int size) {
173      for (int i = 0; i < arr.Length; i++) {
174        arr[i] = new double[size];
175      }
176    }
177
178    public static double[][] TransformToDistances(double[][] similarityMatrix) {
179      double[][] dm = new double[similarityMatrix.Length][];
180
181      for (int i = 0; i < dm.Length; i++) {
182        dm[i] = new double[similarityMatrix.Length];
183        for (int j = 0; j < dm.Length; j++) {
184          dm[i][j] = Math.Sqrt(similarityMatrix[i][i] + similarityMatrix[j][j] - 2 * similarityMatrix[i][j]);
185        }
186      }
187
188      return dm;
189    }
190  }
191}
Note: See TracBrowser for help on using the repository browser.