import java.util.*; |
protected int timeIndex; |
protected int timeIndex = 1; |
private Queue<Update> updateQueue = new LinkedList<Update>(); private Update nextUpdate = null; |
* @param weights - the nubmer of weights found in a node |
* @param weights - the number of weights found in a node |
// if(lattice[i] > maxLatticeDim?) { // maxLatticeDim? = lattice[i]; // } //end if |
//Initializes? each node randomly, not very well done |
//I have found that in a 2D map with equal dimensions //that if you set each weight outward from the center //at a distance of about a quarter, and then guassian //it from there with a .5 = dimension/4, that it works //alot better, but my code is far to complex and messy //to include here |
for(int i=0; i<nodes.length;i++) { |
for(int i=0; i<size();i++) { |
public int size() { return nodes.length; } public void addUpdate(double[] input, int index) { updateQueue.offer(new Update(input,index)); } public void distributeUpdate(int num) { if((nextUpdate == null || nextUpdate.startNode > size()) && !updateQueue.isEmpty()) { nextUpdate = updateQueue.poll(); bmu = getBMU(nextUpdate.weights); timeIndex++; } if(nextUpdate == null) return; for(int i=nextUpdate.startNode; i<size() && i < (nextUpdate.startNode + num); i++) { nodes[i].update(nextUpdate.weights, nextUpdate.index); nextUpdate.startNode++; } } |
for(int i=0; i<nodes.length; i++) { |
for(int i=0; i<size(); i++) { |
private class Update { public double[] weights; public int index; public int startNode; public Update(double w[], int i) { weights = w; index = i; startNode = 0; } } |
//Make? this smarter, it should degrade over time, and it determines //The? size of the area to update, it should be based on the physical //size of the map, not the number of nodes (easiest is the smallest dimension) //I find its best if it slowly decreases in size over about 100 to 1000 ticks/updates double variance = 10/map.timeIndex; |
double variance = Math.min(2,Math.log(.2*map.timeIndex+1)+.25); |
//double limiter = Math.max(Math.exp(-(map.timeIndex/1000)),.75); if(neighborhood < 0.001) return; |
//This? makes the whole thing ALOT faster. Remove for precision or if //you wanna test how you bot handles turn skipping. if(neighborhood < 0.0002) return; //Much? more can be done here, this should also be based on time //and it is, however if this is the bmu, then it will get changed //to the input, an undesirable thing later on. I never did this in //my prototype gun, I could of achieved greater precision here |
//This? is just the index updating, very important to multiply in the neighborhood //otherwise it gets very messy later on, not to much you can do here unlike other areas //.7 is purely a magic number I came up with, I find binIndexes / 30 works well too |
// statBin[i] = KTools.rollingAvg(bin[0][i], neighborhood*input, // Math.min(update_num, 5), 100); |
Yah, I realize I could do better for an example, but I really wanna see what everyone else can come up with for this, its a very basic SOM system, I have myself introduced my own distributed update and so on, this is just the data control. If you like I can add things into this, but I am not an expert programmer, and my methods are anything but pretty. --Chase-san |
Yah, I realize I could do better for an example, but I really wanna see what everyone else can come up with for this, its a very basic SOM system, I have myself introduced my own distributed update and so on, this is just the data control. If you like I can add things into this, but I am not an expert programmer, and my methods are anything but pretty. --Chase-san Its still not running 100% when I plug it into my old prototype gun, but mostly cause I think it uses much more well formed code. ;) --Chase-san |
package chase.s2.net; import java.util.*; public class SelfOrganizingMap { protected SelfOrganizedNode[] nodes; protected SelfOrganizedNode bmu; protected int[] lattice; protected int statBinIndexes; protected int weights; protected int timeIndex = 1; private Queue<Update> updateQueue = new LinkedList<Update>(); private Update nextUpdate = null; /** * Creates a self organizing map with a lattice size, stat indexes and * number of node weights as stated. * @param lattice - An array of integers, designating a shape of the lattice * @param statIndexes - number of indexes in output array * @param weights - the number of weights found in a node */ public SelfOrganizingMap(int[] lattice, int statIndexes, int weights) { if(lattice.length < 1) throw new RuntimeException("Lattice requires atleast 1 dimension."); statBinIndexes = statIndexes; this.lattice = lattice; this.weights = weights; int latticeSize = 1; for(int i=0; i<lattice.length; i++) { latticeSize *= lattice[i]; // if(lattice[i] > maxLatticeDim) { // maxLatticeDim = lattice[i]; // } //end if } //end for nodes = new SelfOrganizedNode[latticeSize]; int[] vector = new int[lattice.length]; for(int i=0; i<latticeSize; i++) { nodes[i] = new SelfOrganizedNode(vector, this); initialize(nodes[i], vector); vector[0]++; for(int j=1; j<lattice.length; j++) { if(vector[j-1] >= lattice[j-1]) { vector[j-1] = 0; vector[j]++; } //end if } //end for } //end for } //Notice: In desperate need of a better initialization! private void initialize(SelfOrganizedNode n, int[] position) { for(int i=0; i<weights; i++) n.weights[i] = Math.random(); } public void updateMap(double[] input, int index) { timeIndex++; bmu = getBMU(input); for(int i=0; i<size();i++) { nodes[i].update(input, index); } } public int size() { return nodes.length; } public void addUpdate(double[] input, int index) { updateQueue.offer(new Update(input,index)); } public void distributeUpdate(int num) { if((nextUpdate == null || nextUpdate.startNode > size()) && !updateQueue.isEmpty()) { nextUpdate = updateQueue.poll(); bmu = getBMU(nextUpdate.weights); timeIndex++; } if(nextUpdate == null) return; for(int i=nextUpdate.startNode; i<size() && i < (nextUpdate.startNode + num); i++) { nodes[i].update(nextUpdate.weights, nextUpdate.index); nextUpdate.startNode++; } } public SelfOrganizedNode getBMU(double[] input) { int bmu = 0; double smallestDifference = Double.POSITIVE_INFINITY; //there is no faster way as the data in the nodes changes constantly for(int i=0; i<size(); i++) { double difference = nodes[i].difference(input); //small shortcut, as we need every ounce of speed if(difference < 0.002) return nodes[i]; if(difference < smallestDifference) { bmu = i; smallestDifference = difference; } } return nodes[bmu]; } private class Update { public double[] weights; public int index; public int startNode; public Update(double w[], int i) { weights = w; index = i; startNode = 0; } } } public class SelfOrganizedNode { protected SelfOrganizingMap map; protected int position[]; protected double weights[]; public float statBin[]; protected SelfOrganizedNode(int[] latticePosition, SelfOrganizingMap parent) { position = latticePosition.clone(); map = parent; statBin = new float[map.statBinIndexes]; weights = new double[map.weights]; } public void update(double[] input, int index) { double distance = distance(map.bmu); double variance = Math.min(2,Math.log(.2*map.timeIndex+1)+.25); double neighborhood = Math.exp(-(variance*variance*distance*distance)/2.0); //double limiter = Math.max(Math.exp(-(map.timeIndex/1000)),.75); if(neighborhood < 0.001) return; for(int i=0; i < map.weights; i++) { weights[i] += neighborhood*(input[i] - weights[i]); } for(int i=0; i<map.statBinIndexes; i++) { int diff = Math.abs(index - i); float stat = (float)Math.exp(-(.7*.7*diff*diff)/2.0); statBin[i] += neighborhood*stat; } } protected float distance(SelfOrganizedNode n) { return distance(position, n.position); } protected double difference(double[] input) { return distance(weights, input); } public static final float distance(int[] p, int[] q) { float k, d = 0; for(int i=0; i<p.length; i++) { d += (k=((float)p[i]-(float)q[i]))*k; } return d; } public static final double distance(double[] p, double[] q) { if(p == null || q == null) return Double.POSITIVE_INFINITY; double k, d = 0; for(int i=0; i<p.length; i++) { d += (k=(p[i]-q[i]))*k; } return d; } }
Its still not running 100% when I plug it into my old prototype gun, but mostly cause I think it uses much more well formed code. ;) --Chase-san