view src/samer/units/Histogram.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
/*
 *	Histogram.java
 *
 *	Copyright (c) 2000, 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.units;

import samer.core.*;
import samer.core.types.*;
import samer.core.util.*;
import samer.core.Agent.*;
import samer.tools.*;
import samer.maths.*;
import java.util.*;

/**
	This actually builds several histograms in parallel: one
	for each element of a Vec or double array.  A IMap provides
	the mapping from element values to histogram bins.
	The result is a <i>Matrix</i>, each <i>row</i> of which
	is the histogram for the corresponding element of the
	input vector.
  */

public class Histogram extends Viewable implements Task, Agent
{
	private Vec			input;
	private Matrix			bins;
	private VMap			map;
	private double[][]	binArray;
	private double[]		lbw; // log of bin widths
	private VVector		L;
	private VDouble		sumL;
	private int				N, count;
	private double[]		x;

	public Histogram( Vec input, int binCount)
	{
		super("histogram");

		Shell.push(getNode());

		N = input.size();
		Shell.setAutoRegister(false);
		bins = new Matrix("bins",N,binCount);
		map = new VMap(new LinearMap(binCount));
		L = new VVector("likelihood",N);
		sumL = new VDouble("sumL");
		Shell.setAutoRegister(true);
		count=0; x=input.array();
		lbw = new double[binCount];
		Shell.pop();

		setAgent(this);
		binArray = bins.getArray();
		Shell.registerViewable(this);
	}

	public Matrix getBinMatrix() { return bins; }
	public VVector getLikelihoodVector() { return L; }
	public VDouble getLikelihoodSignal() { return sumL; }

	public void starting() {}
	public void stopping() {}
	public void run()
	{
		IMap	binmap=map.getMap();
		double [] l=L.array();

		if (x!=null) {
			for(int k=0; k<N; k++) {
				int j=binmap.clipInt(x[k]);
				l[k] = -Math.log(++binArray[k][j]) + lbw[j];
			}
		} else {
			Vec.Iterator i=input.iterator();
			for(int k=0; i.more(); k++) {
				int j=binmap.clipInt(i.next());
				l[k] = -Math.log(++binArray[k][j]) + lbw[j];
			}
		}

		count++;
		Mathx.add(l,Math.log(count));
		sumL.value=Mathx.sum(l);

		L.changed();
		sumL.changed();
		bins.changed();
	}

	public void clear() { bins.zero(); bins.changed(); count=0; }
	public double[] normalise() {
		count=(int)Mathx.sum(binArray[0]);
		Shell.print("Histogram: count="+count);
		Shell.print("Computing bin widths...");
		IMap	binmap=map.getMap();

		for (int i=0; i<lbw.length; i++) {
			lbw[i]=Math.log(binmap.inverseFromInt(i+1) - binmap.inverseFromInt(i));
		}
		return lbw;
	}

	public void dispose() {
		bins.dispose();
		map.dispose();
		L.dispose();
		super.dispose();
	}

	public Viewer getViewer() {
		DefaultViewer vwr=new DefaultViewer(this);
		vwr.add(bins.viewable());
		vwr.add(L);
		vwr.add(sumL);
		return vwr;
	}

	public void getCommands(Registry r) {
		r.add("clear").add("normalise");
		r.group(); map.getCommands(r);
	}

	public void execute(String cmd, Environment env) throws Exception {
		if (cmd.equals("clear")) clear();
		else if (cmd.equals("normalise")) normalise();
		map.execute(cmd,env);
	}

	public Equaliser getEqualiser() { return new Equaliser(); }

	/** This is a function which uses a cumulative probability function
		derived from the histogram to compute a transformation that
		results in a uniformly distributed variable. It takes a SNAPSHOT
		of the current histogram map and bin counts. You must update()
		to update from current histogram.
		*/
	public class Equaliser extends VectorFunctionOfVector
	{
		IMap	map;
		int 	N, M;
		Matrix	C;
		double K;
		double [][]P;

		public Equaliser() {
			N=bins.getRowDimension();
			M=bins.getColumnDimension();
			C=new Matrix("cumulative",N,M);
			P=C.getArray();
			update();
		}

		public void dispose() { C.dispose(); }
		public void update() {
			map=Histogram.this.map.getMap();
			K=map.getIntRange();
			for (int i=0; i<N; i++) {
				double T=0;
				for (int j=0; j<M; j++) {
					P[i][j]=(T+=binArray[i][j]);
				}
				Mathx.mul(P[i],1/T);
			}
			C.changed();
		}

		public void apply(double [] x) { apply(x,x); }
		public void apply(double[] x, double[] y) {
			for (int i=0; i<N; i++) {
				double z=K*map.map(x[i]);
				int j=(int)z;
				if (j<0) y[i]=0;
				else if (j>=M) y[i]=1;
				else {
					// linearly interpolate 
					double P1=P[i][j];
					double P0=(j==0)? 0 : P[i][j-1];
					double d=z-j;
					y[i]=(1-d)*P0 + d*P1;
				}
			}
		}
	}
}