001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.math4.neuralnet.twod.util; 019 020import org.apache.commons.math4.neuralnet.DistanceMeasure; 021import org.apache.commons.math4.neuralnet.Neuron; 022import org.apache.commons.math4.neuralnet.twod.NeuronSquareMesh2D; 023 024/** 025 * <a href="http://en.wikipedia.org/wiki/U-Matrix">U-Matrix</a> 026 * visualization of high-dimensional data projection. 027 * The 8 individual inter-units distances will be 028 * {@link #computeImage(NeuronSquareMesh2D) computed}. They will be 029 * stored in additional pixels around each of the original units of the 030 * 2D-map. The additional pixels that lie along a "diagonal" are shared 031 * by <em>two</em> pairs of units: their value will be set to the average 032 * distance between the units belonging to each of the pairs. The value 033 * zero will be stored in the pixel corresponding to the location of a 034 * unit of the 2D-map. 035 * 036 * @since 3.6 037 * @see org.apache.commons.math4.neuralnet.twod.NeuronSquareMesh2D.DataVisualization#getUMatrix() 038 */ 039public class UnifiedDistanceMatrix implements MapVisualization { 040 /** Distance. */ 041 private final DistanceMeasure distance; 042 043 /** 044 * @param distance Distance. 045 */ 046 public UnifiedDistanceMatrix(DistanceMeasure distance) { 047 this.distance = distance; 048 } 049 050 /** 051 * Computes the distances between a unit of the map and its 052 * neighbours. 053 * The image will contain more pixels than the number of neurons 054 * in the given {@code map} because each neuron has 8 neighbours. 055 * The value zero will be stored in the pixels corresponding to 056 * the location of a map unit. 057 * 058 * @param map Map. 059 * @return an image representing the individual distances. 060 */ 061 @Override 062 public double[][] computeImage(NeuronSquareMesh2D map) { 063 final int numRows = map.getNumberOfRows(); 064 final int numCols = map.getNumberOfColumns(); 065 066 final double[][] uMatrix = new double[numRows * 2 + 1][numCols * 2 + 1]; 067 068 // 1. 069 // Fill right and bottom slots of each unit's location with the 070 // distance between the current unit and each of the two neighbours, 071 // respectively. 072 for (int i = 0; i < numRows; i++) { 073 // Current unit's row index in result image. 074 final int iR = 2 * i + 1; 075 076 for (int j = 0; j < numCols; j++) { 077 // Current unit's column index in result image. 078 final int jR = 2 * j + 1; 079 080 final double[] current = map.getNeuron(i, j).getFeatures(); 081 Neuron neighbour; 082 083 // Right neighbour. 084 neighbour = map.getNeuron(i, j, 085 NeuronSquareMesh2D.HorizontalDirection.RIGHT, 086 NeuronSquareMesh2D.VerticalDirection.CENTER); 087 if (neighbour != null) { 088 uMatrix[iR][jR + 1] = distance.applyAsDouble(current, 089 neighbour.getFeatures()); 090 } 091 092 // Bottom-center neighbour. 093 neighbour = map.getNeuron(i, j, 094 NeuronSquareMesh2D.HorizontalDirection.CENTER, 095 NeuronSquareMesh2D.VerticalDirection.DOWN); 096 if (neighbour != null) { 097 uMatrix[iR + 1][jR] = distance.applyAsDouble(current, 098 neighbour.getFeatures()); 099 } 100 } 101 } 102 103 // 2. 104 // Fill the bottom-right slot of each unit's location with the average 105 // of the distances between 106 // * the current unit and its bottom-right neighbour, and 107 // * the bottom-center neighbour and the right neighbour. 108 for (int i = 0; i < numRows; i++) { 109 // Current unit's row index in result image. 110 final int iR = 2 * i + 1; 111 112 for (int j = 0; j < numCols; j++) { 113 // Current unit's column index in result image. 114 final int jR = 2 * j + 1; 115 116 final Neuron current = map.getNeuron(i, j); 117 final Neuron right = map.getNeuron(i, j, 118 NeuronSquareMesh2D.HorizontalDirection.RIGHT, 119 NeuronSquareMesh2D.VerticalDirection.CENTER); 120 final Neuron bottom = map.getNeuron(i, j, 121 NeuronSquareMesh2D.HorizontalDirection.CENTER, 122 NeuronSquareMesh2D.VerticalDirection.DOWN); 123 final Neuron bottomRight = map.getNeuron(i, j, 124 NeuronSquareMesh2D.HorizontalDirection.RIGHT, 125 NeuronSquareMesh2D.VerticalDirection.DOWN); 126 127 final double current2BottomRight = bottomRight == null ? 128 0 : 129 distance.applyAsDouble(current.getFeatures(), 130 bottomRight.getFeatures()); 131 final double right2Bottom = (right == null || 132 bottom == null) ? 133 0 : 134 distance.applyAsDouble(right.getFeatures(), 135 bottom.getFeatures()); 136 137 // Bottom-right slot. 138 uMatrix[iR + 1][jR + 1] = 0.5 * (current2BottomRight + right2Bottom); 139 } 140 } 141 142 // 3. Copy last row into first row. 143 final int lastRow = uMatrix.length - 1; 144 uMatrix[0] = uMatrix[lastRow]; 145 146 // 4. 147 // Copy last column into first column. 148 final int lastCol = uMatrix[0].length - 1; 149 for (int r = 0; r < lastRow; r++) { 150 uMatrix[r][0] = uMatrix[r][lastCol]; 151 } 152 153 return uMatrix; 154 } 155}