view src/samer/models/DiffScaler.java @ 5:b67a33c44de7

Remove some crap, etc
author samer
date Fri, 05 Apr 2019 21:34:25 +0100
parents bf79fb79ee13
children
line wrap: on
line source
/*
 *	Copyright (c) 2002, Samer Abdallah, King's College London.
 *	All rights reserved.
 *
 *	This software is provided AS iS and WITHOUT ANY WARRANTY;
 *	without even the implied warranty of MERCHANTABILITY or
 *	FITNESS FOR A PARTICULAR PURPOSE.
 */

package samer.models;

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

/**
	Differential scaler: scales and offsets each element of a vector
	independently aiming to match a given prior model. This is
	like ICA using a diagonal weight matrix, with a 'zero-mean'
	normalisation built in, though it doesn't actually the mean to
	do this, but some statistic based on the prior model.
*/

public class DiffScaler extends AnonymousTask implements Model
{
	private Model 		M; // models P(s)
	private int			n;
	private Vec			x;
	private VVector	s;
	private VVector	w;		// vector of multipliers
	private VVector	mu;	// vector of offsets
	private VDouble	logA;

	double []		_x, _s, _g, _w, _m, phi;

	public DiffScaler( Vec input, Model M) { this(input); setOutputModel(M); M.setInput(s); }
	public DiffScaler( Vec input) { this(input.size()); setInput(input); }
	public DiffScaler( int N)
	{
		n = N;

		x = null;
		mu = new VVector("mu",n);	mu.addSaver();
		w = new VVector("w",n);		w.addSaver();
		s = new VVector("output",n);
		logA = new VDouble("log|A|");

		_s = s.array();
		_w = w.array();
		_m = mu.array();
		_g = new double[n];
		phi = null;
		reset();
	}

	public int getSize() { return n; }
	public VVector output() { return s; }
	public VVector weights() { return w; }
	public VVector offsets() { return mu; }
	public Model getOutputModel() { return M;	}
	public void setOutputModel(Model m) { M=m;	}
	public void setInput(Vec in) { x=in; _x=x.array(); }
	public void reset() { reset(1.0); }
	public void reset(double k) {
		Mathx.setAll(_w,k); w.changed();
		Mathx.setAll(_m,0); mu.changed();
		logA.set(-sumlog(_w));
	}

	public String toString() { return "DiffScaler("+x+")"; }
	private static double sumlog(double [] x) {
		double S=0;
		for (int i=0; i<x.length; i++) S += Math.log(x[i]);
		return S;
	}

	public void dispose()
	{
		logA.dispose();
		mu.dispose();
		w.dispose();
		s.dispose();
		super.dispose();
	}

	public void infer() {
		for (int i=0; i<n; i++) _s[i] = _w[i]*(_x[i]-_m[i]);
		s.changed();
	}

	public void compute() {
		Mathx.mul(_g,M.getGradient(),_w);
	}

	public double	getEnergy() { return M.getEnergy() + logA.value; }
	public double [] getGradient() { return _g; }

	public Functionx functionx() {
		return new Functionx() {
			Functionx fM=M.functionx();
			double [] s=new double[n];

			public void dispose() { fM.dispose(); }
			public void evaluate(Datum P) { P.f=evaluate(P.x,P.g); }
			public double evaluate(double [] x, double [] g) {
				for (int i=0; i<n; i++) s[i] = _w[i]*(x[i]-_m[i]);
				double E=fM.evaluate(s,g);
				Mathx.mul(g,_w);
				return E+logA.value;
			}
		};
	}

	public void starting() { logA.set(-sumlog(_w)); }
	public void stopping() {}
	public void run() { infer(); }

	public Model.Trainer getTrainer() { return new Trainer(); }
	public Model.Trainer getOffsetTrainer() { return new OffsetTrainer(); }
	public Model.Trainer getScaleTrainer() { return new ScaleTrainer(); }
	public Model.Trainer getTensionedTrainer() { return  new TensionedTrainer(); }


	public class Trainer extends AnonymousTask implements Model.Trainer
	{
		VDouble		rate1=new VDouble("scaleRate",0.0001);
		VDouble		rate2=new VDouble("offsetRate",0.000001);
		double[]		G,H;
		double []	_s;
		double		count; // n


		public Trainer() {
//			n=DiffScaler.this.n;
			_s = s.array();
			G = new double[n];
			H = new double[n];
		}

		public String toString() { return "Trainer:"+DiffScaler.this; }

		public VDouble getScaleRate() { return rate1; }
		public VDouble getOffsetRate() { return rate2; }

		public void reset() { count=0; Mathx.zero(G); Mathx.zero(H); }
		public void accumulate() { accumulate(1); }
		public void accumulate(double w) {
			double [] phi=M.getGradient();
			for (int i=0; i<n; i++) {
				G[i] += w*(phi[i]*_s[i] - 1);
				H[i] += w*phi[i];
			}
			count+=w;
		}

		public void flush() {
			if (count==0) return; // nothing to do

			double eta1 = rate1.value/count;
			double eta2 = rate2.value/count;

			for (int i=0; i<n; i++) {
				_m[i] += eta2*H[i]/_w[i];
				_w[i] *= Math.exp(-eta1*G[i]);
			}
			logA.value+=eta1*Mathx.sum(G);
			logA.changed();
			mu.changed();
			w.changed();
			reset();
		}

		public void oneshot() { reset(); accumulate(); flush(); }
		public void dispose() { rate1.dispose(); rate2.dispose(); }
		public void starting() { reset(); }
		public void run() { accumulate(); flush(); }
	}

	public class ScaleTrainer extends AnonymousTask implements Model.Trainer
	{
		VDouble	scale=new VDouble("scale",0.001);
		VDouble	stretch=new VDouble("stretch",0.001/n);
		double		thresh=Shell.getDouble("anomaly",20*n);
		double[]		G, H, _s;
		double		count; // n


		public ScaleTrainer() {
			_s = s.array();
			G = new double[n];
			// H = new double[n];
		}

		public String toString() { return "ScaleTrainer:"+DiffScaler.this; }

		public VDouble getScaleRate() { return scale; }
		public VDouble getStretchRate() { return stretch; }

		public void reset() { count=0; Mathx.zero(G); }
		public void accumulate() { accumulate(1); }
		public void accumulate(double w) {
			if (M.getEnergy()>thresh) return;
			double [] phi=M.getGradient();
			for (int i=0; i<n; i++) {
				G[i] += w*(phi[i]*_s[i] - 1);
				// H[i] += w*phi[i];
			}
			count+=w;
		}

		public void flush() {
			if (count==0) return; // nothing to do


			{	// filter elements of G
				double beta = stretch.value/scale.value;
				double meanG = Mathx.sum(G)/n;
				for (int i=0; i<n; i++) {
					G[i]=meanG+beta*(G[i]-meanG);
				}
			}

			double alpha = scale.value/count;
			for (int i=0; i<n; i++) {
				double tmp=Math.exp(-alpha*G[i]);
				if (Double.isNaN(tmp)) throw new Error("alt: NaN"+i);
				_w[i] *= tmp;
			}
			logA.value+=alpha*Mathx.sum(G);
			logA.changed();
			w.changed();
			reset();
		}

		public void oneshot() { reset(); accumulate(); flush(); }
		public void dispose() { scale.dispose(); stretch.dispose(); }
		public void starting() { reset(); }
		public void run() { accumulate(); flush(); }
	}

	public class OffsetTrainer extends AnonymousTask implements Model.Trainer
	{
		VDouble		rate2=new VDouble("offsetRate",0.000001);
		double[]		H;
		double		count; // n


		public OffsetTrainer() {
	//		n=DiffScaler.this.n;
			H = new double[n];
		}

		public String toString() { return "OffsetTrainer:"+DiffScaler.this; }

		public VDouble getOffsetRate() { return rate2; }

		public void reset() { count=0; Mathx.zero(H); }
		public void accumulate() { accumulate(1); }
		public void accumulate(double w) {
			double [] phi=M.getGradient();
			for (int i=0; i<n; i++) H[i] += w*phi[i];
			count+=w;
		}

		public void flush() {
			if (count==0) return; // nothing to do

			double eta2 = rate2.value/count;

			for (int i=0; i<n; i++) {
				_m[i] += eta2*H[i]/_w[i];
			}
			mu.changed();
			reset();
		}

		public void oneshot() { reset(); accumulate(1); flush(); }
		public void dispose() { rate2.dispose(); }
		public void starting() { reset(); }
		public void run() { accumulate(1); flush(); }
	}
	
	public class TensionedTrainer extends Trainer
	{
		VDouble		tension=new VDouble("tension",0.01);
		double			lw[]=new double[n];

		public TensionedTrainer() {}

		public String toString() { return "TensionedTrainer:"+DiffScaler.this; }

		public VDouble getTension() { return tension; }

		public void flush() {
			double T=tension.value;
			int i;

			// go through and modify G
			for (i=0; i<n; i++) { lw[i]=Math.log(_w[i]); }
			G[0] -= T*(lw[1]-lw[0]);	i=n-1;
			G[i] -= T*(lw[i-1]-lw[i]); 	i--;
			for (; i>0; i--) {
				G[i] -= T*(lw[i-1] -2*lw[i]+lw[i+1]);
			}

			// do the same for H?
			// may have to modify T.
			H[0] -= T*(_m[1]-_m[0]);	i=n-1;
			H[i] -= T*(_m[i-1]-_m[i]); 	i--;
			for (; i>0; i--) {
				H[i] -= T*(_m[i-1] -2*_m[i]+_m[i+1]);
			}

			super.flush();
		}

		public void dispose() { tension.dispose(); super.dispose(); }
	}
}