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.ml.neuralnet;
019
020import java.util.List;
021import org.apache.commons.math4.exception.NoDataException;
022import org.apache.commons.math4.ml.distance.DistanceMeasure;
023
024/**
025 * Utilities for network maps.
026 *
027 * @since 3.3
028 */
029public class MapUtils {
030    /**
031     * Class contains only static methods.
032     */
033    private MapUtils() {}
034
035    /**
036     * Computes the quantization error.
037     * The quantization error is the average distance between a feature vector
038     * and its "best matching unit" (closest neuron).
039     *
040     * @param data Feature vectors.
041     * @param neurons List of neurons to scan.
042     * @param distance Distance function.
043     * @return the error.
044     * @throws NoDataException if {@code data} is empty.
045     */
046    public static double computeQuantizationError(Iterable<double[]> data,
047                                                  Iterable<Neuron> neurons,
048                                                  DistanceMeasure distance) {
049        final MapRanking rank = new MapRanking(neurons, distance);
050
051        double d = 0;
052        int count = 0;
053        for (double[] f : data) {
054            ++count;
055            d += distance.compute(f, rank.rank(f, 1).get(0).getFeatures());
056        }
057
058        if (count == 0) {
059            throw new NoDataException();
060        }
061
062        return d / count;
063    }
064
065    /**
066     * Computes the topographic error.
067     * The topographic error is the proportion of data for which first and
068     * second best matching units are not adjacent in the map.
069     *
070     * @param data Feature vectors.
071     * @param net Network.
072     * @param distance Distance function.
073     * @return the error.
074     * @throws NoDataException if {@code data} is empty.
075     */
076    public static double computeTopographicError(Iterable<double[]> data,
077                                                 Network net,
078                                                 DistanceMeasure distance) {
079        final MapRanking rank = new MapRanking(net, distance);
080
081        int notAdjacentCount = 0;
082        int count = 0;
083        for (double[] f : data) {
084            ++count;
085            final List<Neuron> p = rank.rank(f, 2);
086            if (!net.getNeighbours(p.get(0)).contains(p.get(1))) {
087                // Increment count if first and second best matching units
088                // are not neighbours.
089                ++notAdjacentCount;
090            }
091        }
092
093        if (count == 0) {
094            throw new NoDataException();
095        }
096
097        return ((double) notAdjacentCount) / count;
098    }
099}