samer@0
|
1 package samer.mds;
|
samer@0
|
2
|
samer@0
|
3 import samer.core.*;
|
samer@0
|
4 import samer.core.types.*;
|
samer@0
|
5 import samer.maths.*;
|
samer@0
|
6 import samer.tools.*;
|
samer@0
|
7
|
samer@0
|
8 /**
|
samer@0
|
9 This is a version that does not use a separete metric and
|
samer@0
|
10 stress function: the stress function is defined directly in terms
|
samer@0
|
11 of the displacement vectors between objects. The distances
|
samer@0
|
12 are now generalised link targets.
|
samer@0
|
13 */
|
samer@0
|
14 public class NewMDS extends MDSBase
|
samer@0
|
15 {
|
samer@0
|
16 Stress stress; // stress function
|
samer@0
|
17
|
samer@0
|
18 public interface Stress {
|
samer@0
|
19 /** accumulate stress due to a link given end points, return gradient
|
samer@0
|
20 in g, return true if gradient is usable, false of gradient is bad (eg singularity) */
|
samer@0
|
21 boolean add( double target, double [] x, double [] y, double [] g);
|
samer@0
|
22
|
samer@0
|
23 /** return normalised stress and reset for next time */
|
samer@0
|
24 double done();
|
samer@0
|
25
|
samer@0
|
26 /** return last value returned by done() */
|
samer@0
|
27 double get();
|
samer@0
|
28 }
|
samer@0
|
29
|
samer@0
|
30 public static class Laplacian implements Stress {
|
samer@0
|
31 double S=0, a=0;
|
samer@0
|
32 int n=0, E;
|
samer@0
|
33
|
samer@0
|
34 public Laplacian(int E) { this.E=E; }
|
samer@0
|
35
|
samer@0
|
36 /** add stress due to link from x to y. Force in link is returned in g.
|
samer@0
|
37 Target is assumed to be a length measured according to a 1-norm.
|
samer@0
|
38 */
|
samer@0
|
39 public boolean add( double target, double [] x, double [] y, double [] g) {
|
samer@0
|
40 double b=0;
|
samer@0
|
41 for (int i=0; i<E; i++) {
|
samer@0
|
42 b+=Math.abs(x[i]-y[i]);
|
samer@0
|
43 g[i]=sgn(x[i]-y[i]);
|
samer@0
|
44 }
|
samer@0
|
45 b += sqr(b/target-1); n++;
|
samer@0
|
46 Mathx.mul(g,(b-target)/(target*target));
|
samer@0
|
47 return true;
|
samer@0
|
48 }
|
samer@0
|
49
|
samer@0
|
50 /** finish accumulating stress terms: reset accumulators for next
|
samer@0
|
51 iteration and return normalised stress (ie per link)
|
samer@0
|
52 */
|
samer@0
|
53 public double done() { S=a/n; a=0; n=0; return S; }
|
samer@0
|
54
|
samer@0
|
55 /** return last stress value */
|
samer@0
|
56 public double get() { return S; }
|
samer@0
|
57 private final static double sqr(double t) {return t*t;}
|
samer@0
|
58 private final static double sgn(double t) {
|
samer@0
|
59 if (t<0) return -1; else if (t>0) return +1; else return 0;
|
samer@0
|
60 }
|
samer@0
|
61 }
|
samer@0
|
62
|
samer@0
|
63 public NewMDS(Matrix p) {
|
samer@0
|
64 super(p);
|
samer@0
|
65 Shell.push(node);
|
samer@0
|
66 stress=new Laplacian(E);
|
samer@0
|
67 Shell.pop();
|
samer@0
|
68 }
|
samer@0
|
69
|
samer@0
|
70 public void run()
|
samer@0
|
71 {
|
samer@0
|
72 double [][] _P=P.getArray();
|
samer@0
|
73 double d, dS;
|
samer@0
|
74 int i, j, k;
|
samer@0
|
75
|
samer@0
|
76 // accumulate all forces on a per object basis
|
samer@0
|
77
|
samer@0
|
78 for (k=0; k<N; k++) Mathx.zero(F[k]); // zero out object forces
|
samer@0
|
79 for (k=0; k<M; k++) { // for each link
|
samer@0
|
80 i=l1[k]; j=l2[k]; // object indices
|
samer@0
|
81 if (stress.add(D[k],_P[j],_P[i],f)) {
|
samer@0
|
82 Mathx.add(E,F[i],f);
|
samer@0
|
83 Mathx.sub(E,F[j],f);
|
samer@0
|
84 }
|
samer@0
|
85 }
|
samer@0
|
86 S.set(stress.done());
|
samer@0
|
87
|
samer@0
|
88 // now move objects at the ends of each link,
|
samer@0
|
89 // but only the first E dimensions
|
samer@0
|
90 for (k=0; k<N; k++) Mathx.muladd(E,_P[k],F[k],rate.value);
|
samer@0
|
91 P.changed();
|
samer@0
|
92 }
|
samer@0
|
93 }
|