/* Copyright (C) 2006 Leonardo Bispo de Oliveira and
* Daniele Sunaga de Oliveira
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package br.com.ibmp.som;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import br.com.ibmp.som.distance.DistanceMethodInterface;
import br.com.ibmp.som.distance.EuclideanDistanceMethod;
import br.com.ibmp.som.exception.SOMException;
import br.com.ibmp.som.listener.SelfOrganizingMapListener;
import br.com.ibmp.som.matrix.SampleVectorInterface;
import br.com.ibmp.som.matrix.WeightMatrix;
import br.com.ibmp.som.matrix.vo.WeightElementVO;
import br.com.ibmp.som.neighbors.GaussianNeighborsMethod;
import br.com.ibmp.som.neighbors.NeighborsMethodInterface;
/**
* Implementation of SOM Algorithm.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
* @version 1.0
*
*/
public class SelfOrganizingMap {
/** Listener array to emit the step end and end events. */
private List<SelfOrganizingMapListener> listeners;
/** Status from self organizing map algorithm. */
private enum Status {START, STOP, PAUSE};
/** Self organizing map status. Should be START, STOP or PAUSE. */
private Status status;
/** Mutex for synchornizing multiple threads. */
private ReentrantLock mutex;
/** Weight matrix that contains the result. */
private WeightMatrix weightMatrix;
/** Contains the sample vector to be rearranged. */
private SampleVectorInterface sampleVector;
/** Time variable. */
private double t;
/** Iteration counter. */
private int iteration;
/** Number of times to be iterate the algorithm. */
private int iterationNumber;
/** Time increment variable. */
private double tPerIteration;
/**
* Constructor.
*
* @param iterationNumber - Number of times to be iterate the algorithm.
* @param sampleVector - Contains the sample vector to be rearranged.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public SelfOrganizingMap(int iterationNumber, SampleVectorInterface sampleVector)
throws SOMException {
if (sampleVector == null)
throw new SOMException("Invalid sample vector");
if (iterationNumber < 1)
throw new SOMException("Invalid Iteration Number");
setIterationNumber(iterationNumber);
this.weightMatrix = null;
this.sampleVector = sampleVector;
mutex = new ReentrantLock();
listeners = new ArrayList<SelfOrganizingMapListener>();
status = Status.STOP;
iteration = 0;
}
/**
* Assessor to set the iteration number.
*
* @param iterationNumber - Number of times to be iterate the algorithm.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public void setIterationNumber(int iterationNumber)
throws SOMException {
if (status == Status.START)
throw new SOMException("The process is already started");
this.iterationNumber = iterationNumber;
iteration = 0;
t = 0.0d;
tPerIteration = 1.0d/iterationNumber;
}
/**
* Assessor to retrieve the iteration number.
*
* @return Number of times to be iterate the algorithm.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
*/
public int getIterationNumber() {
return iterationNumber;
}
/**
* Add a listener to receive step end and end events.
*
* @param listener - Class to receive the events.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
*/
public final void addListener(SelfOrganizingMapListener listener) {
listeners.add(listener);
}
/**
* Remove a listener of this class.
*
* @param listener - Class to be removed.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
*/
public final void removeListener(SelfOrganizingMapListener listener) {
listeners.remove(listener);
}
/**
* This method will start the process iteration.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
protected final void startLearn() throws SOMException {
if (status == Status.START)
throw new SOMException("The process is already started");
mutex.lock();
try {
status = Status.START;
}
finally {
mutex.unlock();
}
while (t <= 1.0d && status == Status.START)
stepLearn();
if (status != Status.PAUSE) {
if (iteration != 0)
emitEnd();
}
}
/**
* Assessor for emiting a step end event.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
*/
private final void emitStepEnd() {
for(SelfOrganizingMapListener listener : listeners)
listener.stepEnd(weightMatrix, iteration);
}
/**
* Assessor for emiting an end event.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
* @throws SOMException
*
*/
private final void emitEnd() throws SOMException {
for(SelfOrganizingMapListener listener: listeners)
listener.end(weightMatrix, iteration);
status = Status.STOP;
setIterationNumber(this.iterationNumber);
}
/**
* Class interface to start the learning process of Self Organizing Map.
*
* @param weightWidth - Matrix weight width.
* @param weightHeight - Matrix weight height.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void startLearn(int weightWidth, int weightHeight)
throws SOMException {
startLearn(weightWidth, weightHeight, new EuclideanDistanceMethod(),
new GaussianNeighborsMethod());
}
/**
* Class interface to start the learning process of Self Organizing Map.
*
* @param weightWidth - Matrix weight width.
* @param weightHeight - Matrix weight height.
* @param distanceMethod - Method to calculate the distance between
* two elements.
* @param neighborsMethod - Method to rearrange all neighbors and also
* the learning method algorithm.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void startLearn(int weightWidth, int weightHeight,
DistanceMethodInterface distanceMethod, NeighborsMethodInterface
neighborsMethod) throws SOMException {
weightMatrix = new WeightMatrix(weightWidth, weightHeight,
sampleVector, neighborsMethod, distanceMethod);
startLearn();
}
/**
* Class interface to do a step learning process of Self Organizing Map.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void stepLearn() throws SOMException {
if (weightMatrix == null)
throw new SOMException("Weight Matrix not initialized");
mutex.lock();
if (t > 1.0d) {
if (iteration != 0)
emitEnd();
return;
}
try {
weightMatrix.executeStepLearn(t);
t += tPerIteration;
iteration++;
emitStepEnd();
}
finally {
mutex.unlock();
}
if (t > 1.0) {
if (iteration != 0)
emitEnd();
}
}
/**
* Class interface to do a step learning process of Self Organizing Map.
*
* @param weightWidth - Matrix weight width.
* @param weightHeight - Matrix weight height.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void stepLearn(int weightWidth, int weightHeight)
throws SOMException {
stepLearn(weightWidth, weightHeight, new EuclideanDistanceMethod(),
new GaussianNeighborsMethod());
}
/**
* Class interface to do a step learning process of Self Organizing Map.
*
* @param weightWidth - Matrix weight width.
* @param weightHeight - Matrix weight height.
* @param distanceMethod - Method to calculate the distance between
* two elements.
* @param neighborsMethod - Method to rearrange all neighbors and also
* the learning method algorithm.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void stepLearn(int weightWidth, int weightHeight,
DistanceMethodInterface distanceMethod, NeighborsMethodInterface
neighborsMethod) throws SOMException {
weightMatrix = new WeightMatrix(weightWidth, weightHeight,
sampleVector, neighborsMethod, distanceMethod);
stepLearn();
}
/**
* Class interface to resume the learning process of Self Organizing Map
* when the process was paused.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void resumeLearn() throws SOMException {
startLearn();
}
/**
* Class interface to stop the learning process of Self Organizing Map.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void stopLearn() throws SOMException {
status = Status.STOP;
try {
Thread.sleep(200);
}
catch (InterruptedException e) {
throw new SOMException("Problems with thread", e);
}
mutex.lock();
try {
setIterationNumber(iterationNumber);
}
finally {
mutex.unlock();
}
}
/**
* Class interface to stop the learning process of Self Organizing Map.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final void pauseLearn() throws SOMException {
if (status == Status.STOP)
throw new SOMException("The process is not started");
status = Status.PAUSE;
}
/**
* After the learning process we must mount the groups for each sample
* element.
*
* @return Mounted groups.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final WeightElementVO[][] mountGroups() throws SOMException {
return mountGroups(sampleVector);
}
/**
* After the learning process we must mount the groups for each sample
* element.
*
* @param sampleVector - Vector that contains all element to be separated
* between the groups.
*
* @return Mounted groups.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
* @throws SOMException
*
*/
public final WeightElementVO[][] mountGroups(SampleVectorInterface
sampleVector) throws SOMException {
if (status == Status.START)
throw new SOMException("The process is already started");
if (weightMatrix == null)
throw new SOMException("The process was not started");
if (sampleVector == null)
throw new SOMException("The sample vector must not be empty");
return weightMatrix.mountGroups(sampleVector);
}
/**
* Assessor for returning the current sample vector.
*
* @return Current sample vector.
*
* @author Leonardo Bispo de Oliveira and Daniele Sunaga de Oliveira.
*
*/
public final SampleVectorInterface getSample() {
return sampleVector;
}
}