All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.actelion.research.calc.SelfOrganizedMap Maven / Gradle / Ivy

There is a newer version: 2024.11.2
Show newest version
/*
 * Copyright (c) 1997 - 2016
 * Actelion Pharmaceuticals Ltd.
 * Gewerbestrasse 16
 * CH-4123 Allschwil, Switzerland
 *
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of the the copyright holder nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * @author Thomas Sander
 */

package com.actelion.research.calc;

import java.awt.Point;
import java.awt.Rectangle;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public abstract class SelfOrganizedMap extends DataProcessor {
	private static final int cModeNeighbourhoodMask = 7;
	public static final int cModeNeighbourhoodGaussean = 0;
	public static final int cModeNeighbourhoodMexicanHat = 1;
	public static final int cModeNeighbourhoodLinear = 2;
	public static final int cModeTopologyUnlimited = 8;
	public static final int cModeGrowDuringOptimization = 16;
	public static final int cModeFastBestMatchFinding = 32;

	protected SOMController	mController;
	protected Object[][]	mReferenceVector;

	protected int			mNX,mNY,mMode;
	private int				mCycle,mCyclesPerNode,mConstantInfluenceCycles,mThreadCount,
							mInputVectorCount,mInputVectorIndex,mCorrectQuickBestMatches;
	private boolean			mFindBestMatchQuickly;
	private double			mMaxRange,mDiagonal;
	protected double[][]	mInfluence;
	private Point[]			mLastBestMatch;
	private int[][]			mSMPSOMIndex,mSMPInfluenceIndex;
	private Rectangle		mSMPInfluenceRect;
	private ExecutorService	mExecutor;
	private SOMWorker[]		mSOMWorker;

	/**
	 * Constructor to be used if SOM interna are read from a SOM file with read()
	 */
	public SelfOrganizedMap() {
		initializeSMP();
		}

	public SelfOrganizedMap(int nx, int ny, int mode) {
		initializeSMP();
		initializeReferenceVectors(nx, ny, mode);
		}

	public void initializeReferenceVectors(int nx, int ny, int mode) {
		mNX = nx;
		mNY = ny;
		mMode = mode;
		mReferenceVector = new Object[nx][ny];

		if (mThreadCount != 1)
			mSMPSOMIndex = getSMPArraySplitting(nx, ny);
		}

	private void initializeSMP() {
		mThreadCount = Runtime.getRuntime().availableProcessors();
		if (mThreadCount != 1) {
			mExecutor = Executors.newFixedThreadPool(mThreadCount);
			mSOMWorker = new SOMWorker[mThreadCount];
			for (int t=0; t dissimilarity) {
						minDissimilarity = dissimilarity;
						bestMatch = mReferenceVector[x][y];
						}
					}
				}
			sum += Math.sqrt(getDissimilarity(randomVector, bestMatch));
			}
		return sum / (double)randomCount;
		}

	protected double getTimeInfluence(double time) {
		return 1.0 - time;
		}

	protected double getNeighbourInfluence(int dx, int dy, double time) {
											// distance is normalized by map diagonal
		double distance = Math.sqrt(dx*dx+dy*dy)/mDiagonal;
		double f = 0.0;

		switch (mMode & cModeNeighbourhoodMask) {
		case cModeNeighbourhoodGaussean:
			f = Math.exp(distance * distance * Math.log(0.001) / (mMaxRange * mMaxRange));
			return (f < 0.001) ? 0.0 : f;
		case cModeNeighbourhoodMexicanHat:
			f = 1.0 - distance * distance / (mMaxRange * mMaxRange);
			return (f < 0.0) ? 0.0 : f;
		case cModeNeighbourhoodLinear:
			f = 1.0 - distance / mMaxRange;
			return (f < 0.0) ? 0.0 : f;
			}

		return f;
		}

	/**
	 * Calculates start and end array indices for individual threads.
	 * @param xCount
	 * @param yCount
	 */
	private int[][] getSMPArraySplitting(int xCount, int yCount) {
		int nodeCount = xCount * yCount;
		int nodesPerThread = nodeCount / mThreadCount;
		int remainingNodes = nodeCount % mThreadCount;
		int[][] index = new int[mThreadCount+1][2];
		for (int i=0; i= xCount) {
				dx -= xCount;
				dy++;
				}
			index[i+1][0] = index[i][0] + dx;
			index[i+1][1] = index[i][1] + dy;
			}
		return index;
		}

	private void optimize(int startCycle, int cycles) {
		mInfluence = null;

		for (int cycle=startCycle; cycle= mInputVectorCount)
				mInputVectorIndex = 0;
			}
		}

	protected void applyInfluences(Object inputVector, Point location) {
			// apply to every node shifts caused by assigned input vector
		int maxRange = (int)(mDiagonal * mMaxRange);
		int x1 = location.x-maxRange;
		int x2 = location.x+maxRange;
		int y1 = location.y-maxRange;
		int y2 = location.y+maxRange;
		if ((mMode & cModeTopologyUnlimited) != 0) {
			if (x2 - x1 >= mNX) {
				x1 = 0;
				x2 = mNX;
				}
			if (y2 - y1 >= mNY) {
				y1 = 0;
				y2 = mNY;
				}

			if (mThreadCount != 1 && maxRange != 0) {
				if (mSMPInfluenceRect == null || mSMPInfluenceRect.width != x2-x1 || mSMPInfluenceRect.height != y2-y1)
					mSMPInfluenceIndex = getSMPArraySplitting(x2-x1, y2-y1);
				mSMPInfluenceRect = new Rectangle(x1, y1, x2-x1, y2-y1);
				applyInfluencesSMP(inputVector, location);
				}
			else {
				for (int x=x1; x mNX / 2)
							dx = mNX - dx;
						if (dy > mNY / 2)
							dy = mNY - dy;
						if (mInfluence[dx][dy] > 0.0)
							updateReference(inputVector, mReferenceVector[x<0?x+mNX:x mNX)
				x2 = mNX;
			if (y1 < 0)
				y1 = 0;
			if (y2 > mNY)
				y2 = mNY;

			if (mThreadCount != 1 && maxRange != 0) {
				mSMPInfluenceRect = new Rectangle(x1, y1, x2-x1, y2-y1);
				mSMPInfluenceIndex = getSMPArraySplitting(x2-x1, y2-y1);
				applyInfluencesSMP(inputVector, location);
				}
			else {
				for (int x=x1; x 0.0)
							updateReference(inputVector, mReferenceVector[x][y], mInfluence[dx][dy]);
						}
					}
				}
			}
		}

	protected void applyInfluencesSMP(Object inputVector, Point location) {
		CountDownLatch doneSignal = new CountDownLatch(mThreadCount);
		for (SOMWorker worker:mSOMWorker) {
			worker.initApplyInfluences(inputVector, location, doneSignal);
			mExecutor.execute(worker);
			}
		try {
			doneSignal.await();
			}
		catch (InterruptedException e) {}
		}

	public Point findBestMatchLocation(Object inputVector) {
		if (inputVector == null)
			return null;

		// used internally during map optimization
			//  and externally to assign any vector to location on completed map
		if (mThreadCount != 1)
			return findBestMatchLocationSMP(inputVector);

		Point minLocation = new Point(-1, -1);
		double minDissimilarity = Double.POSITIVE_INFINITY;
		for (int x=0; x dissimilarity) {
					minDissimilarity = dissimilarity;
					minLocation.x = x;
					minLocation.y = y;
					}
				}
			}
		return minLocation;
		}

	public Point findBestMatchLocationSMP(Object inputVector) {
		if (inputVector == null)
			return null;

		CountDownLatch doneSignal = new CountDownLatch(mThreadCount);
		for (SOMWorker worker:mSOMWorker) {
			worker.initFindBestMatch(inputVector, doneSignal);
			mExecutor.execute(worker);
			}
		try {
			doneSignal.await();
			}
		catch (InterruptedException e) {}

	    Point minLocation = new Point(-1, -1);
		double minDissimilarity = Double.POSITIVE_INFINITY;
		for (SOMWorker worker:mSOMWorker) {
			double dissimilarity = worker.getBestMatchDissimilarity();
			if (minDissimilarity > dissimilarity) {
				minDissimilarity = dissimilarity;
				minLocation = worker.getBestMatchLocation();
				}
			}

		return minLocation;
		}

	public Point findBestMatchLocationQuickly(Object inputVector) {
		if (inputVector == null)
			return null;

		// used internally during map optimization
			//  and externally to assign any vector to location on completed map
		Point minLocation = mLastBestMatch[mInputVectorIndex];
		double minDissimilarity = getDissimilarity(mReferenceVector[minLocation.x][minLocation.y], inputVector);
		boolean[][] locationChecked = new boolean[mNX][mNY];
		locationChecked[minLocation.x][minLocation.y] = true;
		boolean found;
		do {
//System.out.print("["+minLocation.x+","+minLocation.y+"];");
			Point p = minLocation;
			found = false;
			for (int xdif=-1; xdif<2; xdif++) {
				int x = p.x + xdif;
				if ((mMode & cModeTopologyUnlimited) != 0) {
					if (x < 0)
						x = mNX - 1;
					else if (x >= mNX)
						x = 0;
					}
				else if (x < 0 || x >= mNX)
					continue;
				for (int ydif=-1; ydif<2; ydif++) {
					int y = p.y + ydif;
					if ((mMode & cModeTopologyUnlimited) != 0) {
						if (y < 0)
							y = mNY - 1;
						else if (y >= mNY)
							y = 0;
						}
					else if (y < 0 || y >= mNY)
						continue;

					if (!locationChecked[x][y]) {
						locationChecked[x][y] = true;
						double dissimilarity = getDissimilarity(mReferenceVector[x][y], inputVector);
						if (minDissimilarity > dissimilarity) {
							minDissimilarity = dissimilarity;
							minLocation = new Point(x, y);
							found = true;
							}
						}
					}
				}
			} while (found);

//System.out.println();
		return minLocation;
		}

	public double[] findExactMatchLocation(Object inputVector) {
		if (inputVector == null)
			return null;

		Point p = findBestMatchLocation(inputVector);
		double[] location = new double[3];
		location[0] = p.x;
		location[1] = p.y;

		int x1 = p.x - 1;
		int x2 = p.x + 1;
		int y1 = p.y - 1;
		int y2 = p.y + 1;
		if ((mMode & cModeTopologyUnlimited) != 0) {
			if (x1 == -1)
				x1 += mNX;
			if (x2 == mNX)
				x2 = 0;
			if (y1 == -1)
				y1 += mNY;
			if (y2 == mNY)
				y2 = 0;
			}

		double dis0 = Math.sqrt(getDissimilarity(mReferenceVector[p.x][p.y], inputVector));
		if (dis0 > 0.0) {
			if (x1 == -1)
				location[0] += 0.5 * dis0 / Math.sqrt(getDissimilarity(mReferenceVector[x2][p.y], inputVector));
			else if (x2 == mNX)
				location[0] -= 0.5 * dis0 / Math.sqrt(getDissimilarity(mReferenceVector[x1][p.y], inputVector));
			else {
				double dis1 = Math.sqrt(getDissimilarity(mReferenceVector[x1][p.y], inputVector)) - dis0;
				double dis2 = Math.sqrt(getDissimilarity(mReferenceVector[x2][p.y], inputVector)) - dis0;
				if (dis1 + dis2 != 0)
					location[0] += dis1 / (dis1 + dis2) - 0.5;
				}

			if (y1 == -1)
				location[1] += 0.5 * dis0 / Math.sqrt(getDissimilarity(mReferenceVector[p.x][y2], inputVector));
			else if (y2 == mNY)
				location[1] -= 0.5 * dis0 / Math.sqrt(getDissimilarity(mReferenceVector[p.x][y1], inputVector));
			else {
				double dis1 = Math.sqrt(getDissimilarity(mReferenceVector[p.x][y1], inputVector)) - dis0;
				double dis2 = Math.sqrt(getDissimilarity(mReferenceVector[p.x][y2], inputVector)) - dis0;
				if (dis1 + dis2 != 0)
					location[1] += dis1 / (dis1 + dis2) - 0.5;
				}
			}

		location[2] = dis0;	// dissimilarity of match
		return location;
		}

	private void grow() {
			// works currently only with unlimited SOMs
		Object[][] oldReferenceVector = mReferenceVector;
		mReferenceVector = new Object[mNX*2][mNY*2];
		for (int x=0; x");
		writer.newLine();
		writer.write("");
		writer.newLine();
		writer.write("");
		writer.newLine();

		startProgress("Writing SOM Vectors...", 0, mNY);
		for (int y=0; y");
				writer.newLine();
				}
			}
		stopProgress("SOM Vectors Written");
		}

	public void read(BufferedReader reader) throws Exception {
		String theLine = reader.readLine();
		boolean error = !theLine.startsWith(" dissimilarity) {
							mMinDissimilarity = dissimilarity;
							mLocation.x = x;
							mLocation.y = y;
							}
						}
					}
				break;
			case APPLY_INFLUENCES:
				if ((mMode & cModeTopologyUnlimited) != 0) {
					y1 = mSMPInfluenceIndex[mThreadIndex][1];
					y2 = mSMPInfluenceIndex[mThreadIndex+1][1];
					for (int yy=y1; yy<=y2; yy++) {
						int x1 = (yy == y1) ? mSMPInfluenceIndex[mThreadIndex][0] : 0;
						int x2 = (yy == y2) ? mSMPInfluenceIndex[mThreadIndex+1][0] : mSMPInfluenceRect.width;
						int y = yy + mSMPInfluenceRect.y;
						for (int xx=x1; xx mNX / 2)
								dx = mNX - dx;
							if (dy > mNY / 2)
								dy = mNY - dy;
							if (mInfluence[dx][dy] > 0.0)
								updateReference(mInputVector, mReferenceVector[x<0?x+mNX:x 0.0)
								updateReference(mInputVector, mReferenceVector[x][y], mInfluence[dx][dy]);
							}
						}
					}
				break;
				}
			mDoneSignal.countDown();
			}

		public Point getBestMatchLocation() {
			return mLocation;
			}

		public double getBestMatchDissimilarity() {
			return mMinDissimilarity;
			}
		}
	}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy