view src/samer/mds/MDS.java @ 8:5e3cbbf173aa tip

Reorganise some more
author samer
date Fri, 05 Apr 2019 22:41:58 +0100
parents bf79fb79ee13
children
line wrap: on
line source
package samer.mds;

import samer.core.*;
import samer.core.types.*;
import samer.maths.*;
import samer.tools.*;

/**
     This is an alternative version of MDS that uses less memory.
     Rather than keep a record of the force in each link, it accumulates
     the link forces on a per-object basis.
 */
public class MDS extends MDSBase
{
	Metric		metric;		// metric function
	Stress		stress;		// stress function

	public interface Metric {
		/** return distance between x and y, put y-x in r */
		double d(double [] x, double [] y, double [] r);
	}

	public interface Stress {
		/** accumulate stress due to a link and return dStress/dcurrent */
		double add( double target, double current);

		/** return normalised stress and reset for next time */
		double done();

		/** return last value returned by done() */
		double get();
	}

	public static class SamerStress implements Stress {
		double S=0, a=0;
		int			n=0;

		public double add( double target, double current) {
			a += sqr(current/target-1); n++;
			return (current-target)/(target*target);
		}
		public double done() { S=a/n; a=0; n=0; return S; }
		public double get() { return S; }
		private final static double sqr(double t) {return t*t;}
	}

	public MDS(Matrix p)
	{
		super(p);

		Shell.push(node);
		metric=new Euclidean();
		stress=new SamerStress();
		Shell.pop();
	}

	public void setMetric(Metric m) { metric=m; }

	public void run()
	{
		double [][] _P=P.getArray();
		double	d, dS;
		int 		i, j, k;

		// accumulate all forces on a per object basis

		for (k=0; k<N; k++) Mathx.zero(F[k]); // zero out object forces
		for (k=0; k<M; k++) {			// for each link
			i=l1[k]; j=l2[k]; 				// object indices
			d=metric.d(_P[i],_P[j],f); 		// current distance, vector from i to j in f
			dS=stress.add(D[k],d);

			if (d>0) { // ignore if current distance is zero
				// replace f with dS/dd * grad d
				Mathx.mul(f,dS/d);

				// f contains 'force' on j from i
				// accumulate this force for both objects (only E dimensions used)
				Mathx.add(E,F[i],f);
				Mathx.sub(E,F[j],f);
			}
		}
		S.set(stress.done());

		// now move objects at the ends of each link,
		// but only the first E dimensions
		for (k=0; k<N; k++) Mathx.muladd(E,_P[k],F[k],rate.value);
		P.changed();
	}
}